Source code of Windows XP (NT5)
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.

1727 lines
53 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: heap.c
  3. *
  4. * This module contains the routines for a 2-d heap. It is used primarily
  5. * for allocating space for device-format-bitmaps in off-screen memory.
  6. *
  7. * Off-screen bitmaps are a big deal on NT because:
  8. *
  9. * 1) It reduces the working set. Any bitmap stored in off-screen
  10. * memory is a bitmap that isn't taking up space in main memory.
  11. *
  12. * 2) There is a speed win by using the accelerator hardware for
  13. * drawing, in place of NT's GDI code. NT's GDI is written entirely
  14. * in 'C++' and perhaps isn't as fast as it could be.
  15. *
  16. * 3) It leads naturally to nifty tricks that can take advantage of
  17. * the hardware, such as MaskBlt support and cheap double buffering
  18. * for OpenGL.
  19. *
  20. * The heap algorithm employed herein attempts to solve an unsolvable
  21. * problem: the problem of keeping arbitrary sized bitmaps as packed as
  22. * possible in a 2-d space, when the bitmaps can come and go at random.
  23. *
  24. * This problem is due entirely to the nature of the hardware for which this
  25. * driver is written: the hardware treats everything as 2-d quantities. If
  26. * the hardware bitmap pitch could be changed so that the bitmaps could be
  27. * packed linearly in memory, the problem would be infinitely easier (it is
  28. * much easier to track the memory, and the accelerator can be used to re-pack
  29. * the heap to avoid segmentation).
  30. *
  31. * If your hardware can treat bitmaps as one dimensional quantities (as can
  32. * the XGA and ATI), by all means please implement a new off-screen heap.
  33. *
  34. * When the heap gets full, old allocations will automatically be punted
  35. * from off-screen and copied to DIBs, which we'll let GDI draw on.
  36. *
  37. * Note that this heap manages reverse-L shape off-screen memory
  38. * configurations (where the scan pitch is longer than the visible screen,
  39. * such as happens at 800x600 when the scan length must be a multiple of
  40. * 1024).
  41. *
  42. * NOTE: All heap operations must be done under some sort of synchronization,
  43. * whether it's controlled by GDI or explicitly by the driver. All
  44. * the routines in this module assume that they have exclusive access
  45. * to the heap data structures; multiple threads partying in here at
  46. * the same time would be a Bad Thing. (By default, GDI does NOT
  47. * synchronize drawing on device-created bitmaps.)
  48. *
  49. * Copyright (c) 1993-1995 Microsoft Corporation
  50. \**************************************************************************/
  51. #include "precomp.h"
  52. #define OH_ALLOC_SIZE 4000 // Do all memory allocations in 4k chunks
  53. #define OH_QUANTUM 4 // The minimum dimension of an allocation
  54. #define CXCY_SENTINEL 0x7fffffff // The sentinel at the end of the available
  55. // list has this very large 'cxcy' value
  56. // This macro results in the available list being maintained with a
  57. // cx-major, cy-minor sort:
  58. #define CXCY(cx, cy) (((cx) << 16) | (cy))
  59. /******************************Public*Routine******************************\
  60. * OH* pohNewNode
  61. *
  62. * Allocates a basic memory unit in which we'll pack our data structures.
  63. *
  64. * Since we'll have a lot of OH nodes, most of which we will be
  65. * occasionally traversing, we do our own memory allocation scheme to
  66. * keep them densely packed in memory.
  67. *
  68. * It would be the worst possible thing for the working set to simply
  69. * call AtiAllocMem(sizeof(OH)) every time we needed a new node. There
  70. * would be no locality; OH nodes would get scattered throughout memory,
  71. * and as we traversed the available list for one of our allocations,
  72. * it would be far more likely that we would hit a hard page fault.
  73. \**************************************************************************/
  74. OH* pohNewNode(
  75. PDEV* ppdev)
  76. {
  77. LONG i;
  78. LONG cOhs;
  79. OHALLOC* poha;
  80. OH* poh;
  81. if (ppdev->heap.pohFreeList == NULL)
  82. {
  83. // We zero-init to initialize all the OH flags, and to help in
  84. // debugging (we can afford to do this since we'll be doing this
  85. // very infrequently):
  86. poha = AtiAllocMem(LPTR, FL_ZERO_MEMORY, OH_ALLOC_SIZE);
  87. if (poha == NULL)
  88. return(NULL);
  89. // Insert this OHALLOC at the begining of the OHALLOC chain:
  90. poha->pohaNext = ppdev->heap.pohaChain;
  91. ppdev->heap.pohaChain = poha;
  92. // This has a '+ 1' because OHALLOC includes an extra OH in its
  93. // structure declaration:
  94. cOhs = (OH_ALLOC_SIZE - sizeof(OHALLOC)) / sizeof(OH) + 1;
  95. // The big OHALLOC allocation is simply a container for a bunch of
  96. // OH data structures in an array. The new OH data structures are
  97. // linked together and added to the OH free list:
  98. poh = &poha->aoh[0];
  99. for (i = cOhs - 1; i != 0; i--)
  100. {
  101. poh->pohNext = poh + 1;
  102. poh = poh + 1;
  103. }
  104. poh->pohNext = NULL;
  105. ppdev->heap.pohFreeList = &poha->aoh[0];
  106. }
  107. poh = ppdev->heap.pohFreeList;
  108. ppdev->heap.pohFreeList = poh->pohNext;
  109. return(poh);
  110. }
  111. /******************************Public*Routine******************************\
  112. * VOID vOhFreeNode
  113. *
  114. * Frees our basic data structure allocation unit by adding it to a free
  115. * list.
  116. *
  117. \**************************************************************************/
  118. VOID vOhFreeNode(
  119. PDEV* ppdev,
  120. OH* poh)
  121. {
  122. if (poh == NULL)
  123. return;
  124. poh->pohNext = ppdev->heap.pohFreeList;
  125. ppdev->heap.pohFreeList = poh;
  126. poh->ohState = -1;
  127. }
  128. /******************************Public*Routine******************************\
  129. * VOID vCalculateMaximumNonPermanent
  130. *
  131. * Traverses the list of in-use and available rectangles to find the one
  132. * with the maximal area.
  133. *
  134. \**************************************************************************/
  135. VOID vCalculateMaximumNonPermanent(
  136. PDEV* ppdev)
  137. {
  138. OH* poh;
  139. OH* pohSentinel;
  140. LONG lArea;
  141. LONG lMaxArea;
  142. LONG cxMax;
  143. LONG cyMax;
  144. LONG i;
  145. lMaxArea = 0;
  146. cxMax = 0;
  147. cyMax = 0;
  148. // First time through, loop through the list of free available
  149. // rectangles:
  150. pohSentinel = &ppdev->heap.ohFree;
  151. for (i = 2; i != 0; i--)
  152. {
  153. for (poh = pohSentinel->pohNext; poh != pohSentinel; poh = poh->pohNext)
  154. {
  155. ASSERTDD(poh->ohState != OH_PERMANENT,
  156. "Permanent node in free or discardable list");
  157. // We don't have worry about this multiply overflowing
  158. // because we are dealing in physical screen coordinates,
  159. // which will probably never be more than 15 bits:
  160. lArea = poh->cx * poh->cy;
  161. if (lArea > lMaxArea)
  162. {
  163. cxMax = poh->cx;
  164. cyMax = poh->cy;
  165. lMaxArea = lArea;
  166. }
  167. }
  168. // Second time through, loop through the list of discardable
  169. // rectangles:
  170. pohSentinel = &ppdev->heap.ohDiscardable;
  171. }
  172. // All that we are interested in is the dimensions of the rectangle
  173. // that has the largest possible available area (and remember that
  174. // there might not be any possible available area):
  175. ppdev->heap.cxMax = cxMax;
  176. ppdev->heap.cyMax = cyMax;
  177. }
  178. /******************************Public*Routine******************************\
  179. * OH* pohFree
  180. *
  181. * Frees an off-screen heap allocation. The free space will be combined
  182. * with any adjacent free spaces to avoid segmentation of the 2-d heap.
  183. *
  184. * Note: A key idea here is that the data structure for the upper-left-
  185. * most node must be kept at the same physical CPU memory so that
  186. * adjacency links are kept correctly (when two free spaces are
  187. * merged, the lower or right node can be freed).
  188. *
  189. \**************************************************************************/
  190. OH* pohFree(
  191. PDEV* ppdev,
  192. OH* poh)
  193. {
  194. ULONG cxcy;
  195. OH* pohBeside;
  196. OH* pohNext;
  197. OH* pohPrev;
  198. OHSTATE oldState;
  199. if (poh == NULL)
  200. return(NULL);
  201. DISPDBG((15, "Freeing %li x %li at (%li, %li)",
  202. poh->cx, poh->cy, poh->x, poh->y));
  203. #if DEBUG_HEAP
  204. {
  205. RECTL rclBitmap;
  206. RBRUSH_COLOR rbc;
  207. LONG xOffset;
  208. LONG yOffset;
  209. rclBitmap.left = poh->x;
  210. rclBitmap.top = poh->y;
  211. rclBitmap.right = poh->x + poh->cx;
  212. rclBitmap.bottom = poh->y + poh->cy;
  213. xOffset = ppdev->xOffset;
  214. yOffset = ppdev->yOffset;
  215. ppdev->xOffset = 0;
  216. ppdev->yOffset = 0;
  217. ppdev->pfnFillSolid(ppdev, 1, &rclBitmap, LOGICAL_0, rbc,
  218. NULL);
  219. ppdev->xOffset = xOffset;
  220. ppdev->yOffset = yOffset;
  221. }
  222. #endif
  223. oldState = poh->ohState;
  224. if (oldState != OH_DISCARDABLE)
  225. {
  226. // We can remove the 'reserved' status unless we are merely
  227. // deleting a discardable rectangle that was temporarily
  228. // placed in a reserve rectangle:
  229. poh->cxReserved = 0;
  230. poh->cyReserved = 0;
  231. }
  232. // Update the uniqueness to show that space has been freed, so that
  233. // we may decide to see if some DIBs can be moved back into off-screen
  234. // memory:
  235. ppdev->iHeapUniq++;
  236. MergeLoop:
  237. // Try merging with the right sibling:
  238. pohBeside = poh->pohRight;
  239. if ((poh->cxReserved != poh->cx) &&
  240. (pohBeside->ohState == OH_FREE) &&
  241. (pohBeside->cy == poh->cy) &&
  242. (pohBeside->pohUp == poh->pohUp) &&
  243. (pohBeside->pohDown == poh->pohDown) &&
  244. (pohBeside->pohRight->pohLeft != pohBeside))
  245. {
  246. // Add the right rectangle to ours:
  247. poh->cx += pohBeside->cx;
  248. poh->pohRight = pohBeside->pohRight;
  249. // Remove 'pohBeside' from the free list and free it:
  250. pohBeside->pohNext->pohPrev = pohBeside->pohPrev;
  251. pohBeside->pohPrev->pohNext = pohBeside->pohNext;
  252. vOhFreeNode(ppdev, pohBeside);
  253. goto MergeLoop;
  254. }
  255. // Try merging with the lower sibling:
  256. pohBeside = poh->pohDown;
  257. if ((poh->cyReserved != poh->cy) &&
  258. (pohBeside->ohState == OH_FREE) &&
  259. (pohBeside->cx == poh->cx) &&
  260. (pohBeside->pohLeft == poh->pohLeft) &&
  261. (pohBeside->pohRight == poh->pohRight) &&
  262. (pohBeside->pohDown->pohUp != pohBeside))
  263. {
  264. poh->cy += pohBeside->cy;
  265. poh->pohDown = pohBeside->pohDown;
  266. pohBeside->pohNext->pohPrev = pohBeside->pohPrev;
  267. pohBeside->pohPrev->pohNext = pohBeside->pohNext;
  268. vOhFreeNode(ppdev, pohBeside);
  269. goto MergeLoop;
  270. }
  271. // Don't do any more merge this rectangle into anything to the
  272. // top or to the left if it's reserved:
  273. if (!poh->cxReserved)
  274. {
  275. // Try merging with the left sibling:
  276. pohBeside = poh->pohLeft;
  277. if ((pohBeside->cxReserved != pohBeside->cx) &&
  278. (pohBeside->ohState == OH_FREE) &&
  279. (pohBeside->cy == poh->cy) &&
  280. (pohBeside->pohUp == poh->pohUp) &&
  281. (pohBeside->pohDown == poh->pohDown) &&
  282. (pohBeside->pohRight == poh) &&
  283. (poh->pohRight->pohLeft != poh))
  284. {
  285. // We add our rectangle to the one to the left:
  286. pohBeside->cx += poh->cx;
  287. pohBeside->pohRight = poh->pohRight;
  288. // Remove 'poh' from whatever list it was in (if we were
  289. // asked to free a 'permanent' node, it will have been in
  290. // the permanent list) and free it:
  291. poh->pohNext->pohPrev = poh->pohPrev;
  292. poh->pohPrev->pohNext = poh->pohNext;
  293. vOhFreeNode(ppdev, poh);
  294. poh = pohBeside;
  295. goto MergeLoop;
  296. }
  297. // Try merging with the upper sibling:
  298. pohBeside = poh->pohUp;
  299. if ((pohBeside->cyReserved != pohBeside->cy) &&
  300. (pohBeside->ohState == OH_FREE) &&
  301. (pohBeside->cx == poh->cx) &&
  302. (pohBeside->pohLeft == poh->pohLeft) &&
  303. (pohBeside->pohRight == poh->pohRight) &&
  304. (pohBeside->pohDown == poh) &&
  305. (poh->pohDown->pohUp != poh))
  306. {
  307. pohBeside->cy += poh->cy;
  308. pohBeside->pohDown = poh->pohDown;
  309. poh->pohNext->pohPrev = poh->pohPrev;
  310. poh->pohPrev->pohNext = poh->pohNext;
  311. vOhFreeNode(ppdev, poh);
  312. poh = pohBeside;
  313. goto MergeLoop;
  314. }
  315. }
  316. // Remove this node from whatever list it's in:
  317. poh->pohNext->pohPrev = poh->pohPrev;
  318. poh->pohPrev->pohNext = poh->pohNext;
  319. cxcy = CXCY(poh->cx, poh->cy);
  320. // Insert the node, in order, into the free list:
  321. pohNext = ppdev->heap.ohFree.pohNext;
  322. while (pohNext->cxcy < cxcy)
  323. {
  324. pohNext = pohNext->pohNext;
  325. }
  326. pohPrev = pohNext->pohPrev;
  327. pohPrev->pohNext = poh;
  328. pohNext->pohPrev = poh;
  329. poh->pohPrev = pohPrev;
  330. poh->pohNext = pohNext;
  331. poh->cxcy = cxcy;
  332. poh->ohState = OH_FREE;
  333. if (oldState == OH_PERMANENT)
  334. {
  335. // Removing the permanent entry means that we may be able to
  336. // enlarge the maximum possible rectangle we can allow:
  337. vCalculateMaximumNonPermanent(ppdev);
  338. }
  339. // Return the node pointer for the new and improved available rectangle:
  340. return(poh);
  341. }
  342. /******************************Public*Routine******************************\
  343. * BOOL bDiscardEverythingInRectangle
  344. *
  345. * Throws out of the heap any discardable bitmaps that intersect with the
  346. * specified rectangle.
  347. *
  348. \**************************************************************************/
  349. BOOL bDiscardEverythingInRectangle(
  350. PDEV* ppdev,
  351. LONG x,
  352. LONG y,
  353. LONG cx,
  354. LONG cy)
  355. {
  356. BOOL bRet;
  357. OH* poh;
  358. OH* pohNext;
  359. bRet = TRUE; // Assume success
  360. poh = ppdev->heap.ohDiscardable.pohNext;
  361. while (poh != &ppdev->heap.ohDiscardable)
  362. {
  363. ASSERTDD(poh->ohState == OH_DISCARDABLE,
  364. "Non-discardable node in discardable list");
  365. pohNext = poh->pohNext;
  366. if ((poh->x < x + cx) &&
  367. (poh->y < y + cy) &&
  368. (poh->x + poh->cx > x) &&
  369. (poh->y + poh->cy > y))
  370. {
  371. // The two rectangles intersect. Give the boot to the
  372. // discardable bitmap:
  373. if (!pohMoveOffscreenDfbToDib(ppdev, poh))
  374. bRet = FALSE;
  375. }
  376. poh = pohNext;
  377. }
  378. return(bRet);
  379. }
  380. /******************************Public*Routine******************************\
  381. * BOOL bFreeRightAndBottomSpace
  382. *
  383. * Given a free off-screen rectangle, allocates the upper-left part of
  384. * the rectangle to hold the allocation request, and puts the two rectangles
  385. * comprising the unused right and bottom portions on the free list.
  386. *
  387. \**************************************************************************/
  388. BOOL bFreeRightAndBottomSpace(
  389. PDEV* ppdev,
  390. OH* pohThis,
  391. LONG cxThis,
  392. LONG cyThis,
  393. BOOL bQuantum) // Set if inifitely small allocations should be
  394. // allowed
  395. {
  396. ULONG cxcy; // Temporary versions
  397. OH* pohNext;
  398. OH* pohPrev;
  399. LONG cxRem;
  400. LONG cyRem;
  401. OH* pohBelow;
  402. LONG cxBelow;
  403. LONG cyBelow;
  404. OH* pohBeside;
  405. LONG cxBeside;
  406. LONG cyBeside;
  407. LONG cQuantum;
  408. // We're going to use the upper-left corner of our given rectangle,
  409. // and divide the unused remainder into two rectangles which will
  410. // go on the free list.
  411. // Compute the width of the unused rectangle to the right, and the
  412. // height of the unused rectangle below:
  413. cyRem = pohThis->cy - cyThis;
  414. cxRem = pohThis->cx - cxThis;
  415. // Given finite area, we wish to find the two rectangles that are
  416. // most square -- i.e., the arrangement that gives two rectangles
  417. // with the least perimiter:
  418. cyBelow = cyRem;
  419. cxBeside = cxRem;
  420. if (cxRem <= cyRem)
  421. {
  422. cxBelow = cxThis + cxRem;
  423. cyBeside = cyThis;
  424. }
  425. else
  426. {
  427. cxBelow = cxThis;
  428. cyBeside = cyThis + cyRem;
  429. }
  430. // If 'bQuantum' is set, we only make new available rectangles of
  431. // the unused right and bottom portions if they're greater in
  432. // dimension than OH_QUANTUM (it hardly makes sense to do the
  433. // book-work to keep around a 2-pixel wide available space, for
  434. // example):
  435. cQuantum = (bQuantum) ? 1 : OH_QUANTUM;
  436. pohBeside = NULL;
  437. if (cxBeside >= cQuantum)
  438. {
  439. pohBeside = pohNewNode(ppdev);
  440. if (pohBeside == NULL)
  441. return(FALSE);
  442. }
  443. pohBelow = NULL;
  444. if (cyBelow >= cQuantum)
  445. {
  446. pohBelow = pohNewNode(ppdev);
  447. if (pohBelow == NULL)
  448. {
  449. vOhFreeNode(ppdev, pohBeside);
  450. return(FALSE);
  451. }
  452. // Insert this rectangle into the available list (which is
  453. // sorted on ascending cxcy):
  454. cxcy = CXCY(cxBelow, cyBelow);
  455. pohNext = ppdev->heap.ohFree.pohNext;
  456. while (pohNext->cxcy < cxcy)
  457. {
  458. pohNext = pohNext->pohNext;
  459. }
  460. pohPrev = pohNext->pohPrev;
  461. pohPrev->pohNext = pohBelow;
  462. pohNext->pohPrev = pohBelow;
  463. pohBelow->pohPrev = pohPrev;
  464. pohBelow->pohNext = pohNext;
  465. // Now update the adjacency information:
  466. pohBelow->pohLeft = pohThis->pohLeft;
  467. pohBelow->pohUp = pohThis;
  468. pohBelow->pohRight = pohThis->pohRight;
  469. pohBelow->pohDown = pohThis->pohDown;
  470. // Update the rest of the new node information:
  471. pohBelow->cxReserved = 0;
  472. pohBelow->cyReserved = 0;
  473. pohBelow->cxcy = cxcy;
  474. pohBelow->ohState = OH_FREE;
  475. pohBelow->x = pohThis->x;
  476. pohBelow->y = pohThis->y + cyThis;
  477. pohBelow->cx = cxBelow;
  478. pohBelow->cy = cyBelow;
  479. // Modify the current node to reflect the changes we've made:
  480. pohThis->cy = cyThis;
  481. }
  482. if (cxBeside >= cQuantum)
  483. {
  484. // Insert this rectangle into the available list (which is
  485. // sorted on ascending cxcy):
  486. cxcy = CXCY(cxBeside, cyBeside);
  487. pohNext = ppdev->heap.ohFree.pohNext;
  488. while (pohNext->cxcy < cxcy)
  489. {
  490. pohNext = pohNext->pohNext;
  491. }
  492. pohPrev = pohNext->pohPrev;
  493. pohPrev->pohNext = pohBeside;
  494. pohNext->pohPrev = pohBeside;
  495. pohBeside->pohPrev = pohPrev;
  496. pohBeside->pohNext = pohNext;
  497. // Now update the adjacency information:
  498. pohBeside->pohUp = pohThis->pohUp;
  499. pohBeside->pohLeft = pohThis;
  500. pohBeside->pohDown = pohThis->pohDown;
  501. pohBeside->pohRight = pohThis->pohRight;
  502. // Update the rest of the new node information:
  503. pohBeside->cxReserved = 0;
  504. pohBeside->cyReserved = 0;
  505. pohBeside->cxcy = cxcy;
  506. pohBeside->ohState = OH_FREE;
  507. pohBeside->x = pohThis->x + cxThis;
  508. pohBeside->y = pohThis->y;
  509. pohBeside->cx = cxBeside;
  510. pohBeside->cy = cyBeside;
  511. // Modify the current node to reflect the changes we've made:
  512. pohThis->cx = cxThis;
  513. }
  514. if (pohBelow != NULL)
  515. {
  516. pohThis->pohDown = pohBelow;
  517. if ((pohBeside != NULL) && (cyBeside == pohThis->cy))
  518. pohBeside->pohDown = pohBelow;
  519. }
  520. if (pohBeside != NULL)
  521. {
  522. pohThis->pohRight = pohBeside;
  523. if ((pohBelow != NULL) && (cxBelow == pohThis->cx))
  524. pohBelow->pohRight = pohBeside;
  525. }
  526. pohThis->cxcy = CXCY(pohThis->cx, pohThis->cy);
  527. return(TRUE);
  528. }
  529. /******************************Public*Routine******************************\
  530. * OH* pohMakeRoomAtLocation
  531. *
  532. * Attempts to allocate a rectangle at a specific position.
  533. *
  534. \**************************************************************************/
  535. OH* pohMakeRoomAtLocation(
  536. PDEV* ppdev,
  537. POINTL* pptl, // Requested position for the rectangle
  538. LONG cxThis, // Width of rectangle to be allocated
  539. LONG cyThis, // Height of rectangle to be allocated
  540. FLONG floh) // Allocation flags
  541. {
  542. OH* poh;
  543. OH* pohTop;
  544. OH* pohLeft;
  545. LONG cxLeft;
  546. LONG cyTop;
  547. if (!(floh & FLOH_ONLY_IF_ROOM))
  548. {
  549. // First off, discard any bitmaps that overlap the requested
  550. // rectangle, assuming we're allowed to:
  551. if (!bDiscardEverythingInRectangle(ppdev, pptl->x, pptl->y, cxThis, cyThis))
  552. return(NULL);
  553. }
  554. // Now see if there is a free rectangle that entirely contains the
  555. // requested rectangle.
  556. for (poh = ppdev->heap.ohFree.pohNext;
  557. poh != &ppdev->heap.ohFree;
  558. poh = poh->pohNext)
  559. {
  560. ASSERTDD(poh->ohState == OH_FREE, "Non-free node in free list");
  561. // See if the current free rectangle completely contains the
  562. // requested rectangle:
  563. if ((poh->x <= pptl->x) &&
  564. (poh->y <= pptl->y) &&
  565. (poh->x + poh->cx >= pptl->x + cxThis) &&
  566. (poh->y + poh->cy >= pptl->y + cyThis))
  567. {
  568. // We can't reserve this rectangle, or make it permanent, if it's
  569. // already been reserved:
  570. if ((!poh->cxReserved) ||
  571. ((floh & (FLOH_RESERVE | FLOH_MAKE_PERMANENT)) == 0))
  572. {
  573. // The 'poh' rectangle entirely contains the requested
  574. // rectangle. We may have a situation like this, where
  575. // the smaller rectangle is the requested rectangle, and
  576. // the larger rectangle is the available rectangle:
  577. //
  578. // +-------------------+
  579. // | |
  580. // | +---------+ |
  581. // | |Requested| |
  582. // | | | |
  583. // | +---------+ |
  584. // | |
  585. // +-------------------+
  586. //
  587. // We want to make the space to the left and to the top of
  588. // the requested rectangle available to the heap. Our
  589. // free-space routine only knows how to free space to the
  590. // right and bottom of an allocation, though. So we will
  591. // temporarily allocate temporary rectangles to subdivide
  592. // our rectangle like the following:
  593. //
  594. // +-------------------+
  595. // |Top |
  596. // +----+--------------+
  597. // |Left|Free |
  598. // | | |
  599. // | | |
  600. // | | |
  601. // +----+--------------+
  602. //
  603. // Then, in the resulting 'Free' space, we will allocate the
  604. // upper-left corner for our requested rectangle, after which
  605. // we will go back and free the 'Top' and 'Left' temporary
  606. // rectangles.
  607. pohTop = NULL;
  608. pohLeft = NULL;
  609. cxLeft = pptl->x - poh->x;
  610. cyTop = pptl->y - poh->y;
  611. if (cyTop > 0)
  612. {
  613. if (!bFreeRightAndBottomSpace(ppdev, poh, poh->cx, cyTop,
  614. TRUE))
  615. {
  616. return(NULL);
  617. }
  618. pohTop = poh;
  619. poh = pohTop->pohDown;
  620. }
  621. if (cxLeft > 0)
  622. {
  623. if (!bFreeRightAndBottomSpace(ppdev, poh, cxLeft, poh->cy,
  624. TRUE))
  625. {
  626. pohFree(ppdev, pohTop);
  627. return(NULL);
  628. }
  629. pohLeft = poh;
  630. poh = pohLeft->pohRight;
  631. }
  632. ASSERTDD((poh->x == pptl->x) &&
  633. (poh->y == pptl->y) &&
  634. (poh->x + poh->cx >= poh->x + cxThis) &&
  635. (poh->y + poh->cy >= poh->y + cyThis),
  636. "poh must properly fit requested rectangle");
  637. // Finally, we can subdivide to get our requested rectangle:
  638. if (!bFreeRightAndBottomSpace(ppdev, poh, cxThis, cyThis, FALSE))
  639. poh = NULL; // Fail this call
  640. // Free our temporary rectangles, if there are any:
  641. pohFree(ppdev, pohTop);
  642. pohFree(ppdev, pohLeft);
  643. return(poh);
  644. }
  645. }
  646. }
  647. // There was no free rectangle that completely contains the requested
  648. // rectangle:
  649. return(NULL);
  650. }
  651. /******************************Public*Routine******************************\
  652. * OH* pohMakeRoomAnywhere
  653. *
  654. * Allocates space for an off-screen rectangle. It will attempt to find
  655. * the smallest available free rectangle, and will allocate the block out
  656. * of its upper-left corner. The remaining two rectangles will be placed
  657. * on the available free space list.
  658. *
  659. * If the rectangle would have been large enough to fit into off-screen
  660. * memory, but there is not enough available free space, we will boot
  661. * bitmaps out of off-screen and into DIBs until there is enough room.
  662. *
  663. \**************************************************************************/
  664. OH* pohMakeRoomAnywhere(
  665. PDEV* ppdev,
  666. LONG cxThis, // Width of rectangle to be allocated
  667. LONG cyThis, // Height of rectangle to be allocated
  668. FLONG floh) // May have FLOH_ONLY_IF_ROOM set
  669. {
  670. ULONG cxcyThis; // Width and height search key
  671. OH* pohThis; // Points to found available rectangle we'll use
  672. ASSERTDD((cxThis > 0) && (cyThis > 0), "Illegal allocation size");
  673. // Increase the width to get the proper alignment (thus ensuring that all
  674. // allocations will be properly aligned):
  675. cxThis = (cxThis + (HEAP_X_ALIGNMENT - 1)) & ~(HEAP_X_ALIGNMENT - 1);
  676. // We can't succeed if the requested rectangle is larger than the
  677. // largest possible available rectangle:
  678. if ((cxThis > ppdev->heap.cxMax) || (cyThis > ppdev->heap.cyMax))
  679. return(NULL);
  680. // Find the first available rectangle the same size or larger than
  681. // the requested one:
  682. cxcyThis = CXCY(cxThis, cyThis);
  683. pohThis = ppdev->heap.ohFree.pohNext;
  684. while (pohThis->cxcy < cxcyThis)
  685. {
  686. ASSERTDD(pohThis->ohState == OH_FREE, "Non-free node in free list");
  687. pohThis = pohThis->pohNext;
  688. }
  689. while (pohThis->cy < cyThis)
  690. {
  691. ASSERTDD(pohThis->ohState == OH_FREE, "Non-free node in free list");
  692. pohThis = pohThis->pohNext;
  693. }
  694. ASSERTDD(pohThis->ohState == OH_FREE, "Non-free node in free list");
  695. if (pohThis->cxcy == CXCY_SENTINEL)
  696. {
  697. // There was no space large enough...
  698. if (floh & FLOH_ONLY_IF_ROOM)
  699. return(NULL);
  700. DISPDBG((15, "> Making room for %li x %li allocation...", cxThis, cyThis));
  701. // We couldn't find an available rectangle that was big enough
  702. // to fit our request. So throw things out of the heap until we
  703. // have room, oldest allocations first:
  704. do {
  705. pohThis = ppdev->heap.ohDiscardable.pohPrev; // Least-recently created
  706. ASSERTDD(pohThis != &ppdev->heap.ohDiscardable,
  707. "Ran out of discardable entries -- Max not set correctly");
  708. ASSERTDD(pohThis->ohState == OH_DISCARDABLE,
  709. "Non-discardable node in discardable list");
  710. // We can safely exit here if we have to:
  711. pohThis = pohMoveOffscreenDfbToDib(ppdev, pohThis);
  712. if (pohThis == NULL)
  713. return(NULL);
  714. } while ((pohThis->cx < cxThis) || (pohThis->cy < cyThis));
  715. }
  716. if ((pohThis->cxReserved) && (floh & (FLOH_RESERVE | FLOH_MAKE_PERMANENT)))
  717. {
  718. // We can't reserve this rectangle, or make it permanent, if it's
  719. // already been reserved. So throw absolutely everything out and
  720. // search the free list.
  721. //
  722. // NOTE: This is extremely painful! A better approach would be to
  723. // keep separate 'cxMax' and 'cyMax' variables kept for free
  724. // rectangles that are not reserved (cxMax and cyMax
  725. // currently include reserved free rectangles).
  726. if (!bDiscardEverythingInRectangle(ppdev, 0, 0,
  727. ppdev->cxMemory, ppdev->cyMemory))
  728. {
  729. return(NULL);
  730. }
  731. pohThis = &ppdev->heap.ohFree;
  732. do {
  733. pohThis = pohThis->pohNext;
  734. if (pohThis == &ppdev->heap.ohFree)
  735. return(NULL);
  736. } while ((pohThis->cxReserved) ||
  737. (pohThis->cx < cxThis) ||
  738. (pohThis->cy < cyThis));
  739. }
  740. if (!bFreeRightAndBottomSpace(ppdev, pohThis, cxThis, cyThis, FALSE))
  741. return(NULL);
  742. return(pohThis);
  743. }
  744. /******************************Public*Routine******************************\
  745. * OH* pohAllocate
  746. *
  747. * Allocates a rectangle in off-screen memory.
  748. *
  749. * Types:
  750. *
  751. * FLOH_RESERVE
  752. *
  753. * Reserves an off-screen rectangle. The space may still be used by
  754. * discardable bitmaps until the rectangle is committed via 'bOhCommit'.
  755. *
  756. * FLOH_MAKE_PERMANENT
  757. *
  758. * Allocates an off-screen rectangle that can never be booted
  759. * of the heap. It's the caller's responsibility to manage
  760. * the rectangle, which includes what to do with the memory in
  761. * DrvAssertMode when the display is changed to full-screen
  762. * mode.
  763. *
  764. * Default
  765. *
  766. * Allocates a 'discardable' off-screen rectangle for a DFB that may
  767. * be kicked out of off-screen if the space is needed.
  768. *
  769. * Options:
  770. *
  771. * FLOH_ONLY_IF_ROOM
  772. *
  773. * Allocates an off-screen rectangle only if there is free space
  774. * available -- i.e., no discardable rectangles will be moved out of
  775. * off-screen to make room.
  776. *
  777. * Default
  778. *
  779. * May move discardable rectangles out of off-screen to make room.
  780. *
  781. * Arguments:
  782. *
  783. * pptl
  784. *
  785. * If NULL, the rectangle will be allocated anywhere in un-used offscreen
  786. * memory.
  787. *
  788. * If non-NULL, is a requested position for the rectangle.
  789. *
  790. * NOTE: The heap will quickly fragment if arbitrary positions are
  791. * requested. This position option works best if there is only
  792. * one specific rectangle ever requested, or if the allocations
  793. * are always wider than they are high.
  794. *
  795. \**************************************************************************/
  796. OH* pohAllocate(
  797. PDEV* ppdev,
  798. POINTL* pptl, // Optional requested position of rectangle
  799. LONG cxThis, // Width of rectangle to be allocated
  800. LONG cyThis, // Height of rectangle to be allocated
  801. FLOH floh) // Allocation flags
  802. {
  803. OH* pohThis; // Points to found available rectangle we'll use
  804. OH* pohRoot; // Point to root of list where we'll insert node
  805. ULONG cxcy;
  806. OH* pohNext;
  807. OH* pohPrev;
  808. ASSERTDD((floh & (FLOH_RESERVE | FLOH_MAKE_PERMANENT))
  809. != (FLOH_RESERVE | FLOH_MAKE_PERMANENT),
  810. "Illegal flags -- can't set both FLOH_RESERVE and FLOH_MAKE_PERMANENT");
  811. if (pptl == NULL)
  812. {
  813. pohThis = pohMakeRoomAnywhere(ppdev, cxThis, cyThis, floh);
  814. if (pohThis == NULL)
  815. DISPDBG((15, "Can't allocate %li x %li with flags %li",
  816. cxThis, cyThis, floh));
  817. }
  818. else
  819. {
  820. pohThis = pohMakeRoomAtLocation(ppdev, pptl, cxThis, cyThis, floh);
  821. if (pohThis == NULL)
  822. DISPDBG((15, "Can't allocate %li x %li at %li, %li with flags %li",
  823. cxThis, cyThis, pptl->x, pptl->y, floh));
  824. }
  825. if (pohThis == NULL)
  826. return(NULL);
  827. // Calculate the effective start address for this bitmap in off-
  828. // screen memory:
  829. pohThis->pvScan0 = ppdev->pjScreen + (pohThis->y * ppdev->lDelta)
  830. + (pohThis->x * ppdev->cjPelSize);
  831. // The caller is responsible for setting this field:
  832. pohThis->pdsurf = NULL;
  833. // Our 'reserve' logic expects the node to have 'free' status:
  834. ASSERTDD(pohThis->ohState == OH_FREE, "Node not free after making room");
  835. ASSERTDD(((floh & (FLOH_RESERVE | FLOH_MAKE_PERMANENT)) == 0) ||
  836. (pohThis->cxReserved == 0),
  837. "Can't reserve a rectangle that's already reserved");
  838. if (floh & FLOH_RESERVE)
  839. {
  840. // A non-zero value for 'cxReserved' means it's reserved:
  841. pohThis->cxReserved = pohThis->cx;
  842. pohThis->cyReserved = pohThis->cy;
  843. // Remove this node from its place in the free list:
  844. pohThis->pohPrev->pohNext = pohThis->pohNext;
  845. pohThis->pohNext->pohPrev = pohThis->pohPrev;
  846. // Now insert the node, in order, back into the free list:
  847. cxcy = pohThis->cxcy;
  848. pohNext = ppdev->heap.ohFree.pohNext;
  849. while (pohNext->cxcy < cxcy)
  850. {
  851. pohNext = pohNext->pohNext;
  852. }
  853. pohPrev = pohNext->pohPrev;
  854. pohPrev->pohNext = pohThis;
  855. pohNext->pohPrev = pohThis;
  856. pohThis->pohPrev = pohPrev;
  857. pohThis->pohNext = pohNext;
  858. }
  859. else
  860. {
  861. // Remove this node from the free list:
  862. pohThis->pohPrev->pohNext = pohThis->pohNext;
  863. pohThis->pohNext->pohPrev = pohThis->pohPrev;
  864. if (floh & FLOH_MAKE_PERMANENT)
  865. {
  866. // Change status of node and insert into permanent list:
  867. pohThis->ohState = OH_PERMANENT;
  868. pohRoot = &ppdev->heap.ohPermanent;
  869. // Calculate the new maximum size rectangle available
  870. // for allocation:
  871. vCalculateMaximumNonPermanent(ppdev);
  872. }
  873. else
  874. {
  875. // Change status of node and insert into discardable list:
  876. pohThis->ohState = OH_DISCARDABLE;
  877. pohRoot = &ppdev->heap.ohDiscardable;
  878. }
  879. // Now insert the node at the head of the appropriate list:
  880. pohThis->pohNext = pohRoot->pohNext;
  881. pohThis->pohPrev = pohRoot;
  882. pohRoot->pohNext->pohPrev = pohThis;
  883. pohRoot->pohNext = pohThis;
  884. }
  885. DISPDBG((15, " Allocated (%li x %li) at (%li, %li) with flags %li",
  886. cxThis, cyThis, pohThis->x, pohThis->y, floh));
  887. return(pohThis);
  888. }
  889. /******************************Public*Routine******************************\
  890. * BOOL bOhCommit
  891. *
  892. * If 'bCommit' is TRUE, converts a 'reserved' allocation to 'permanent,'
  893. * moving from off-screen memory any discardable allocations that may have
  894. * been using the space.
  895. *
  896. * If 'bCommit' is FALSE, converts a 'permanent' allocation to 'reserved,'
  897. * allowing the space to be used by discardable allocations.
  898. *
  899. \**************************************************************************/
  900. BOOL bOhCommit(
  901. PDEV* ppdev,
  902. OH* poh,
  903. BOOL bCommit)
  904. {
  905. BOOL bRet;
  906. ULONG cxcy;
  907. OH* pohRoot;
  908. OH* pohNext;
  909. OH* pohPrev;
  910. bRet = FALSE; // Assume failure
  911. if (poh == NULL)
  912. return(bRet);
  913. if ((bCommit) && (poh->cxReserved))
  914. {
  915. if (bDiscardEverythingInRectangle(ppdev, poh->x, poh->y,
  916. poh->cxReserved, poh->cyReserved))
  917. {
  918. DISPDBG((15, "Commited %li x %li at (%li, %li)",
  919. poh->cx, poh->cy, poh->x, poh->y));
  920. poh->ohState = OH_PERMANENT;
  921. // Remove this node from the free list:
  922. poh->pohPrev->pohNext = poh->pohNext;
  923. poh->pohNext->pohPrev = poh->pohPrev;
  924. // Now insert the node at the head of the permanent list:
  925. pohRoot = &ppdev->heap.ohPermanent;
  926. poh->pohNext = pohRoot->pohNext;
  927. poh->pohPrev = pohRoot;
  928. pohRoot->pohNext->pohPrev = poh;
  929. pohRoot->pohNext = poh;
  930. bRet = TRUE;
  931. }
  932. }
  933. else if ((!bCommit) && (poh->ohState == OH_PERMANENT))
  934. {
  935. DISPDBG((15, "Decommited %li x %li at (%li, %li)",
  936. poh->cx, poh->cy, poh->x, poh->y));
  937. poh->ohState = OH_FREE;
  938. poh->cxReserved = poh->cx;
  939. poh->cyReserved = poh->cy;
  940. // Remove this node from the permanent list:
  941. poh->pohPrev->pohNext = poh->pohNext;
  942. poh->pohNext->pohPrev = poh->pohPrev;
  943. // Now insert the node, in order, into the free list:
  944. cxcy = poh->cxcy;
  945. pohNext = ppdev->heap.ohFree.pohNext;
  946. while (pohNext->cxcy < cxcy)
  947. {
  948. pohNext = pohNext->pohNext;
  949. }
  950. pohPrev = pohNext->pohPrev;
  951. pohPrev->pohNext = poh;
  952. pohNext->pohPrev = poh;
  953. poh->pohPrev = pohPrev;
  954. poh->pohNext = pohNext;
  955. bRet = TRUE;
  956. }
  957. // Recalculate the biggest rectangle available for allocation:
  958. vCalculateMaximumNonPermanent(ppdev);
  959. return(bRet);
  960. }
  961. /******************************Public*Routine******************************\
  962. * BOOL bMoveDibToOffscreenDfbIfRoom
  963. *
  964. * Converts the DIB DFB to an off-screen DFB, if there's room for it in
  965. * off-screen memory.
  966. *
  967. * Returns: FALSE if there wasn't room, TRUE if successfully moved.
  968. *
  969. \**************************************************************************/
  970. BOOL bMoveDibToOffscreenDfbIfRoom(
  971. PDEV* ppdev,
  972. DSURF* pdsurf)
  973. {
  974. OH* poh;
  975. SURFOBJ* pso;
  976. RECTL rclDst;
  977. POINTL ptlSrc;
  978. HSURF hsurf;
  979. ASSERTDD(pdsurf->dt == DT_DIB,
  980. "Can't move a bitmap off-screen when it's already off-screen");
  981. // If we're in full-screen mode, we can't move anything to off-screen
  982. // memory:
  983. if (!ppdev->bEnabled)
  984. return(FALSE);
  985. poh = pohAllocate(ppdev, NULL, pdsurf->sizl.cx, pdsurf->sizl.cy,
  986. FLOH_ONLY_IF_ROOM);
  987. if (poh == NULL)
  988. {
  989. // There wasn't any free room.
  990. return(FALSE);
  991. }
  992. // 'pdsurf->sizl' is the actual bitmap dimension, not 'poh->cx' or
  993. // 'poh->cy'.
  994. rclDst.left = poh->x;
  995. rclDst.top = poh->y;
  996. rclDst.right = rclDst.left + pdsurf->sizl.cx;
  997. rclDst.bottom = rclDst.top + pdsurf->sizl.cy;
  998. ptlSrc.x = 0;
  999. ptlSrc.y = 0;
  1000. ppdev->pfnPutBits(ppdev, pdsurf->pso, &rclDst, &ptlSrc);
  1001. // Update the data structures to reflect the new off-screen node:
  1002. pso = pdsurf->pso;
  1003. pdsurf->dt = DT_SCREEN;
  1004. pdsurf->poh = poh;
  1005. poh->pdsurf = pdsurf;
  1006. // Now free the DIB. Get the hsurf from the SURFOBJ before we unlock
  1007. // it (it's not legal to dereference psoDib when it's unlocked):
  1008. hsurf = pso->hsurf;
  1009. EngUnlockSurface(pso);
  1010. EngDeleteSurface(hsurf);
  1011. return(TRUE);
  1012. }
  1013. /******************************Public*Routine******************************\
  1014. * OH* pohMoveOffscreenDfbToDib
  1015. *
  1016. * Converts the DFB from being off-screen to being a DIB.
  1017. *
  1018. * Note: The caller does NOT have to call 'pohFree' on 'poh' after making
  1019. * this call.
  1020. *
  1021. * Returns: NULL if the function failed (due to a memory allocation).
  1022. * Otherwise, it returns a pointer to the coalesced off-screen heap
  1023. * node that has been made available for subsequent allocations
  1024. * (useful when trying to free enough memory to make a new
  1025. * allocation).
  1026. \**************************************************************************/
  1027. OH* pohMoveOffscreenDfbToDib(
  1028. PDEV* ppdev,
  1029. OH* poh)
  1030. {
  1031. DSURF* pdsurf;
  1032. HBITMAP hbmDib;
  1033. SURFOBJ* pso;
  1034. RECTL rclDst;
  1035. POINTL ptlSrc;
  1036. DISPDBG((15, "Throwing out %li x %li at (%li, %li)!",
  1037. poh->cx, poh->cy, poh->x, poh->y));
  1038. pdsurf = poh->pdsurf;
  1039. ASSERTDD((poh->x != 0) || (poh->y != 0),
  1040. "Can't make the visible screen into a DIB");
  1041. ASSERTDD(pdsurf->dt != DT_DIB,
  1042. "Can't make a DIB into even more of a DIB");
  1043. hbmDib = EngCreateBitmap(pdsurf->sizl, 0, ppdev->iBitmapFormat,
  1044. BMF_TOPDOWN, NULL);
  1045. if (hbmDib)
  1046. {
  1047. if (EngAssociateSurface((HSURF) hbmDib, ppdev->hdevEng, 0))
  1048. {
  1049. pso = EngLockSurface((HSURF) hbmDib);
  1050. if (pso != NULL)
  1051. {
  1052. rclDst.left = 0;
  1053. rclDst.top = 0;
  1054. rclDst.right = pdsurf->sizl.cx;
  1055. rclDst.bottom = pdsurf->sizl.cy;
  1056. ptlSrc.x = poh->x;
  1057. ptlSrc.y = poh->y;
  1058. ppdev->pfnGetBits(ppdev, pso, &rclDst, &ptlSrc);
  1059. pdsurf->dt = DT_DIB;
  1060. pdsurf->pso = pso;
  1061. // Don't even bother checking to see if this DIB should
  1062. // be put back into off-screen memory until the next
  1063. // heap 'free' occurs:
  1064. pdsurf->iUniq = ppdev->iHeapUniq;
  1065. pdsurf->cBlt = 0;
  1066. // Remove this node from the off-screen DFB list, and free
  1067. // it. 'pohFree' will never return NULL:
  1068. return(pohFree(ppdev, poh));
  1069. }
  1070. }
  1071. // Fail case:
  1072. EngDeleteSurface((HSURF) hbmDib);
  1073. }
  1074. return(NULL);
  1075. }
  1076. /******************************Public*Routine******************************\
  1077. * BOOL bMoveEverythingFromOffscreenToDibs
  1078. *
  1079. * This function is used when we're about to enter full-screen mode, which
  1080. * would wipe all our off-screen bitmaps. GDI can ask us to draw on
  1081. * device bitmaps even when we're in full-screen mode, and we do NOT have
  1082. * the option of stalling the call until we switch out of full-screen.
  1083. * We have no choice but to move all the off-screen DFBs to DIBs.
  1084. *
  1085. * Returns TRUE if all DSURFs have been successfully moved.
  1086. *
  1087. \**************************************************************************/
  1088. BOOL bMoveAllDfbsFromOffscreenToDibs(
  1089. PDEV* ppdev)
  1090. {
  1091. // Throw out any discardable bitmaps over the entire surface:
  1092. return(bDiscardEverythingInRectangle(ppdev, 0, 0,
  1093. ppdev->cxMemory, ppdev->cyMemory));
  1094. }
  1095. /******************************Public*Routine******************************\
  1096. * HBITMAP DrvCreateDeviceBitmap
  1097. *
  1098. * Function called by GDI to create a device-format-bitmap (DFB). We will
  1099. * always try to allocate the bitmap in off-screen; if we can't, we simply
  1100. * fail the call and GDI will create and manage the bitmap itself.
  1101. *
  1102. * Note: We do not have to zero the bitmap bits. GDI will automatically
  1103. * call us via DrvBitBlt to zero the bits (which is a security
  1104. * consideration).
  1105. *
  1106. \**************************************************************************/
  1107. HBITMAP DrvCreateDeviceBitmap(
  1108. DHPDEV dhpdev,
  1109. SIZEL sizl,
  1110. ULONG iFormat)
  1111. {
  1112. PDEV* ppdev;
  1113. OH* poh;
  1114. DSURF* pdsurf;
  1115. HBITMAP hbmDevice;
  1116. FLONG flHooks;
  1117. //return 0; //RKE:
  1118. ppdev = (PDEV*) dhpdev;
  1119. // If we're in full-screen mode, we hardly have any off-screen memory
  1120. // in which to allocate a DFB. LATER: We could still allocate an
  1121. // OH node and put the bitmap on the DIB DFB list for later promotion.
  1122. if (!ppdev->bEnabled)
  1123. return(0);
  1124. // We only support device bitmaps that are the same colour depth
  1125. // as our display.
  1126. //
  1127. // Actually, those are the only kind GDI will ever call us with,
  1128. // but we may as well check. Note that this implies you'll never
  1129. // get a crack at 1bpp bitmaps.
  1130. if (iFormat != ppdev->iBitmapFormat)
  1131. return(0);
  1132. // We don't want anything 8x8 or smaller -- they're typically brush
  1133. // patterns which we don't particularly want to stash in off-screen
  1134. // memory:
  1135. if ((sizl.cx <= 8) && (sizl.cy <= 8))
  1136. return(0);
  1137. // Since 24bpp mach32 is GDI-managed, and we don't have access to
  1138. // vGetBits and vPutBits as a consequence, we reject device bitmaps.
  1139. if (iFormat == BMF_24BPP && ppdev->iAsic != ASIC_88800GX)
  1140. return 0;
  1141. poh = pohAllocate(ppdev, NULL, sizl.cx, sizl.cy, 0);
  1142. if (poh != NULL)
  1143. {
  1144. pdsurf = AtiAllocMem(LMEM_FIXED, 0, sizeof(DSURF));
  1145. if (pdsurf != NULL)
  1146. {
  1147. hbmDevice = EngCreateDeviceBitmap((DHSURF) pdsurf, sizl, iFormat);
  1148. if (hbmDevice != NULL)
  1149. {
  1150. flHooks = ppdev->flHooks;
  1151. // Setting the SYNCHRONIZEACCESS flag tells GDI that we
  1152. // want all drawing to the bitmaps to be synchronized (GDI
  1153. // is multi-threaded and by default does not synchronize
  1154. // device bitmap drawing -- it would be a Bad Thing for us
  1155. // to have multiple threads using the accelerator at the
  1156. // same time):
  1157. flHooks |= HOOK_SYNCHRONIZEACCESS;
  1158. // It's a device-managed surface; make sure we don't set
  1159. // HOOK_SYNCHRONIZE, otherwise we may confuse GDI:
  1160. flHooks &= ~HOOK_SYNCHRONIZE;
  1161. if (EngAssociateSurface((HSURF) hbmDevice, ppdev->hdevEng,
  1162. flHooks))
  1163. {
  1164. pdsurf->dt = DT_SCREEN;
  1165. pdsurf->poh = poh;
  1166. pdsurf->sizl = sizl;
  1167. pdsurf->ppdev = ppdev;
  1168. poh->pdsurf = pdsurf;
  1169. return(hbmDevice);
  1170. }
  1171. EngDeleteSurface((HSURF) hbmDevice);
  1172. }
  1173. AtiFreeMem(pdsurf);
  1174. }
  1175. pohFree(ppdev, poh);
  1176. }
  1177. return(0);
  1178. }
  1179. /******************************Public*Routine******************************\
  1180. * VOID DrvDeleteDeviceBitmap
  1181. *
  1182. * Deletes a DFB.
  1183. *
  1184. \**************************************************************************/
  1185. VOID DrvDeleteDeviceBitmap(
  1186. DHSURF dhsurf)
  1187. {
  1188. DSURF* pdsurf;
  1189. PDEV* ppdev;
  1190. SURFOBJ* psoDib;
  1191. HSURF hsurfDib;
  1192. pdsurf = (DSURF*) dhsurf;
  1193. ppdev = pdsurf->ppdev;
  1194. if (pdsurf->dt == DT_SCREEN)
  1195. {
  1196. pohFree(ppdev, pdsurf->poh);
  1197. }
  1198. else
  1199. {
  1200. ASSERTDD(pdsurf->dt == DT_DIB, "Expected DIB type");
  1201. psoDib = pdsurf->pso;
  1202. // Get the hsurf from the SURFOBJ before we unlock it (it's not
  1203. // legal to dereference psoDib when it's unlocked):
  1204. hsurfDib = psoDib->hsurf;
  1205. EngUnlockSurface(psoDib);
  1206. EngDeleteSurface(hsurfDib);
  1207. }
  1208. AtiFreeMem(pdsurf);
  1209. }
  1210. /******************************Public*Routine******************************\
  1211. * BOOL bAssertModeOffscreenHeap
  1212. *
  1213. * This function is called whenever we switch in or out of full-screen
  1214. * mode. We have to convert all the off-screen bitmaps to DIBs when
  1215. * we switch to full-screen (because we may be asked to draw on them even
  1216. * when in full-screen, and the mode switch would probably nuke the video
  1217. * memory contents anyway).
  1218. *
  1219. \**************************************************************************/
  1220. BOOL bAssertModeOffscreenHeap(
  1221. PDEV* ppdev,
  1222. BOOL bEnable)
  1223. {
  1224. BOOL b = TRUE;
  1225. if (!bEnable)
  1226. {
  1227. b = bMoveAllDfbsFromOffscreenToDibs(ppdev);
  1228. }
  1229. return b;
  1230. }
  1231. /******************************Public*Routine******************************\
  1232. * VOID vDisableOffscreenHeap
  1233. *
  1234. * Frees any resources allocated by the off-screen heap.
  1235. *
  1236. \**************************************************************************/
  1237. VOID vDisableOffscreenHeap(
  1238. PDEV* ppdev)
  1239. {
  1240. OHALLOC* poha;
  1241. OHALLOC* pohaNext;
  1242. SURFOBJ* psoPunt;
  1243. HSURF hsurf;
  1244. psoPunt = ppdev->psoPunt;
  1245. if (psoPunt != NULL)
  1246. {
  1247. hsurf = psoPunt->hsurf;
  1248. EngUnlockSurface(psoPunt);
  1249. EngDeleteSurface(hsurf);
  1250. }
  1251. psoPunt = ppdev->psoPunt2;
  1252. if (psoPunt != NULL)
  1253. {
  1254. hsurf = psoPunt->hsurf;
  1255. EngUnlockSurface(psoPunt);
  1256. EngDeleteSurface(hsurf);
  1257. }
  1258. poha = ppdev->heap.pohaChain;
  1259. while (poha != NULL)
  1260. {
  1261. pohaNext = poha->pohaNext; // Grab the next pointer before it's freed
  1262. AtiFreeMem(poha);
  1263. poha = pohaNext;
  1264. }
  1265. }
  1266. /******************************Public*Routine******************************\
  1267. * BOOL bEnableOffscreenHeap
  1268. *
  1269. * Initializes the off-screen heap using all available video memory,
  1270. * accounting for the portion taken by the visible screen.
  1271. *
  1272. * Input: ppdev->cxScreen
  1273. * ppdev->cyScreen
  1274. * ppdev->cxMemory
  1275. * ppdev->cyMemory
  1276. *
  1277. \**************************************************************************/
  1278. BOOL bEnableOffscreenHeap(
  1279. PDEV* ppdev)
  1280. {
  1281. OH* poh;
  1282. SIZEL sizl;
  1283. HSURF hsurf;
  1284. POINTL ptlScreen;
  1285. DISPDBG((15, "Screen: %li x %li Memory: %li x %li",
  1286. ppdev->cxScreen, ppdev->cyScreen, ppdev->cxMemory, ppdev->cyMemory));
  1287. ASSERTDD((ppdev->cxScreen <= ppdev->cxMemory) &&
  1288. (ppdev->cyScreen <= ppdev->cyMemory),
  1289. "Memory must not have smaller dimensions than visible screen!");
  1290. ppdev->heap.pohaChain = NULL;
  1291. ppdev->heap.pohFreeList = NULL;
  1292. // Initialize the available list, which will be a circular
  1293. // doubly-linked list kept in ascending 'cxcy' order, with a
  1294. // 'sentinel' at the end of the list:
  1295. poh = pohNewNode(ppdev);
  1296. if (poh == NULL)
  1297. goto ReturnFalse;
  1298. // The first node describes the entire video memory size:
  1299. poh->pohNext = &ppdev->heap.ohFree;
  1300. poh->pohPrev = &ppdev->heap.ohFree;
  1301. poh->ohState = OH_FREE;
  1302. poh->x = 0;
  1303. poh->y = 0;
  1304. poh->cx = ppdev->cxMemory;
  1305. poh->cy = ppdev->cyMemory;
  1306. poh->cxcy = CXCY(ppdev->cxMemory, ppdev->cyMemory);
  1307. poh->pohLeft = &ppdev->heap.ohFree;
  1308. poh->pohUp = &ppdev->heap.ohFree;
  1309. poh->pohRight = &ppdev->heap.ohFree;
  1310. poh->pohDown = &ppdev->heap.ohFree;
  1311. poh->pvScan0 = ppdev->pjScreen;
  1312. // The second node is our free list sentinel:
  1313. ppdev->heap.ohFree.pohNext = poh;
  1314. ppdev->heap.ohFree.pohPrev = poh;
  1315. ppdev->heap.ohFree.cxcy = CXCY_SENTINEL;
  1316. ppdev->heap.ohFree.cx = 0x7fffffff;
  1317. ppdev->heap.ohFree.cy = 0x7fffffff;
  1318. ppdev->heap.ohFree.ohState = OH_FREE;
  1319. // Initialize the discardable list, which will be a circular
  1320. // doubly-linked list kept in order, with a sentinel at the end.
  1321. // This node is also used for the screen-surface, for its offset:
  1322. ppdev->heap.ohDiscardable.pohNext = &ppdev->heap.ohDiscardable;
  1323. ppdev->heap.ohDiscardable.pohPrev = &ppdev->heap.ohDiscardable;
  1324. ppdev->heap.ohDiscardable.ohState = OH_DISCARDABLE;
  1325. // Initialize the permanent list, which will be a circular
  1326. // doubly-linked list kept in order, with a sentinel at the end.
  1327. ppdev->heap.ohPermanent.pohNext = &ppdev->heap.ohPermanent;
  1328. ppdev->heap.ohPermanent.pohPrev = &ppdev->heap.ohPermanent;
  1329. ppdev->heap.ohPermanent.ohState = OH_PERMANENT;
  1330. // For the moment, make the max really big so that the first
  1331. // allocation we're about to do will succeed:
  1332. ppdev->heap.cxMax = 0x7fffffff;
  1333. ppdev->heap.cyMax = 0x7fffffff;
  1334. ptlScreen.x = 0;
  1335. ptlScreen.y = 0;
  1336. // Finally, reserve the upper-left corner for the screen. We can
  1337. // actually throw away 'poh' because we'll never need it again
  1338. // (not even for disabling the off-screen heap since everything is
  1339. // freed using OHALLOCs):
  1340. poh = pohAllocate(ppdev, &ptlScreen, ppdev->cxScreen, ppdev->cyScreen,
  1341. FLOH_MAKE_PERMANENT);
  1342. ASSERTDD((poh != NULL) && (poh->x == 0) && (poh->y == 0) &&
  1343. (poh->cx >= ppdev->cxScreen) && (poh->cy >= ppdev->cyScreen),
  1344. "Screen allocation messed up");
  1345. // Remember it so that we can associate the screen SURFOBJ with this
  1346. // poh:
  1347. ppdev->pohScreen = poh;
  1348. // Allocate a 'punt' SURFOBJ we'll use when the device-bitmap is in
  1349. // off-screen memory, but we want GDI to draw to it directly as an
  1350. // engine-managed surface:
  1351. sizl.cx = ppdev->cxMemory;
  1352. sizl.cy = ppdev->cyMemory;
  1353. // We want to create it with exactly the same hooks and capabilities
  1354. // as our primary surface. We will override the 'lDelta' and 'pvScan0'
  1355. // fields later:
  1356. hsurf = (HSURF) EngCreateBitmap(sizl,
  1357. 0xbadf00d,
  1358. ppdev->iBitmapFormat,
  1359. BMF_TOPDOWN,
  1360. (VOID*) 0xbadf00d);
  1361. if ((hsurf == 0) ||
  1362. (!EngAssociateSurface(hsurf, ppdev->hdevEng, ppdev->flHooks)) ||
  1363. (!(ppdev->psoPunt = EngLockSurface(hsurf))))
  1364. {
  1365. DISPDBG((0, "Failed punt surface creation"));
  1366. EngDeleteSurface(hsurf);
  1367. goto ReturnFalse;
  1368. }
  1369. // We need another for doing DrvBitBlt and DrvCopyBits when both
  1370. // surfaces are off-screen bitmaps:
  1371. hsurf = (HSURF) EngCreateBitmap(sizl,
  1372. 0xbadf00d,
  1373. ppdev->iBitmapFormat,
  1374. BMF_TOPDOWN,
  1375. (VOID*) 0xbadf00d);
  1376. if ((hsurf == 0) ||
  1377. (!EngAssociateSurface(hsurf, ppdev->hdevEng, ppdev->flHooks)) ||
  1378. (!(ppdev->psoPunt2 = EngLockSurface(hsurf))))
  1379. {
  1380. DISPDBG((0, "Failed punt surface creation"));
  1381. EngDeleteSurface(hsurf);
  1382. goto ReturnFalse;
  1383. }
  1384. DISPDBG((5, "Passed bEnableOffscreenHeap"));
  1385. if (poh != NULL)
  1386. return(TRUE);
  1387. ReturnFalse:
  1388. DISPDBG((0, "Failed bEnableOffscreenHeap"));
  1389. return(FALSE);
  1390. }
  1391.