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.

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