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.

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