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.

1220 lines
36 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: heap.c
  3. *
  4. * This module contains the routines for a 2-d heap. It is used primarily
  5. * for allocating space for device-format-bitmaps in off-screen memory.
  6. *
  7. * Off-screen bitmaps are a big deal on NT because:
  8. *
  9. * 1) It reduces the working set. Any bitmap stored in off-screen
  10. * memory is a bitmap that isn't taking up space in main memory.
  11. *
  12. * 2) There is a speed win by using the accelerator hardware for
  13. * drawing, in place of NT's GDI code. NT's GDI is written entirely
  14. * in 'C++' and perhaps isn't as fast as it could be.
  15. *
  16. * 3) It leads naturally to nifty tricks that can take advantage of
  17. * the hardware, such as MaskBlt support and cheap double buffering
  18. * for OpenGL.
  19. *
  20. * The heap algorithm employed herein attempts to solve an unsolvable
  21. * problem: the problem of keeping arbitrary sized bitmaps as packed as
  22. * possible in a 2-d space, when the bitmaps can come and go at random.
  23. *
  24. * This problem is due entirely to the nature of the hardware for which this
  25. * driver is written: the hardware treats everything as 2-d quantities. If
  26. * the hardware bitmap pitch could be changed so that the bitmaps could be
  27. * packed linearly in memory, the problem would be infinitely easier (it is
  28. * much easier to track the memory, and the accelerator can be used to re-pack
  29. * the heap to avoid segmentation).
  30. *
  31. * If your hardware can treat bitmaps as one dimensional quantities (as can
  32. * the XGA and ATI), by all means please implement a new off-screen heap.
  33. *
  34. * When the heap gets full, old allocations will automatically be punted
  35. * from off-screen and copied to DIBs, which we'll let GDI draw on.
  36. *
  37. * Note that this heap manages reverse-L shape off-screen memory
  38. * configurations (where the scan pitch is longer than the visible screen,
  39. * such as happens at 800x600 when the scan length must be a multiple of
  40. * 1024).
  41. *
  42. * NOTE: All heap operations must be done under some sort of synchronization,
  43. * whether it's controlled by GDI or explicitly by the driver. All
  44. * the routines in this module assume that they have exclusive access
  45. * to the heap data structures; multiple threads partying in here at
  46. * the same time would be a Bad Thing. (By default, GDI does NOT
  47. * synchronize drawing on device-created bitmaps.)
  48. *
  49. * Copyright (c) 1993-1995 Microsoft Corporation
  50. \**************************************************************************/
  51. #include "precomp.h"
  52. #define OH_ALLOC_SIZE 4000 // Do all memory allocations in 4k chunks
  53. #define OH_QUANTUM 8 // 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->ofl = 0;
  127. }
  128. /******************************Public*Routine******************************\
  129. * OH* pohFree
  130. *
  131. * Frees an off-screen heap allocation. The free space will be combined
  132. * with any adjacent free spaces to avoid segmentation of the 2-d heap.
  133. *
  134. * Note: A key idea here is that the data structure for the upper-left-
  135. * most node must be kept at the same physical CPU memory so that
  136. * adjacency links are kept correctly (when two free spaces are
  137. * merged, the lower or right node can be freed).
  138. *
  139. \**************************************************************************/
  140. OH* pohFree(
  141. PDEV* ppdev,
  142. OH* poh)
  143. {
  144. ULONG cxcy;
  145. OH* pohBeside;
  146. OH* pohNext;
  147. OH* pohPrev;
  148. if (poh == NULL)
  149. return(NULL);
  150. DISPDBG((1, "Freeing %li x %li at (%li, %li)",
  151. poh->cx, poh->cy, poh->x, poh->y));
  152. // Update the uniqueness to show that space has been freed, so that
  153. // we may decide to see if some DIBs can be moved back into off-screen
  154. // memory:
  155. ppdev->iHeapUniq++;
  156. MergeLoop:
  157. ASSERTDD(!(poh->ofl & OFL_PERMANENT), "Can't free permanents for now");
  158. // Try merging with the right sibling:
  159. pohBeside = poh->pohRight;
  160. if ((pohBeside->ofl & OFL_AVAILABLE) &&
  161. (pohBeside->cy == poh->cy) &&
  162. (pohBeside->pohUp == poh->pohUp) &&
  163. (pohBeside->pohDown == poh->pohDown) &&
  164. (pohBeside->pohRight->pohLeft != pohBeside))
  165. {
  166. // Add the right rectangle to ours:
  167. poh->cx += pohBeside->cx;
  168. poh->pohRight = pohBeside->pohRight;
  169. // Remove 'pohBeside' from the ??? list and free it:
  170. pohBeside->pohNext->pohPrev = pohBeside->pohPrev;
  171. pohBeside->pohPrev->pohNext = pohBeside->pohNext;
  172. vOhFreeNode(ppdev, pohBeside);
  173. goto MergeLoop;
  174. }
  175. // Try merging with the lower sibling:
  176. pohBeside = poh->pohDown;
  177. if ((pohBeside->ofl & OFL_AVAILABLE) &&
  178. (pohBeside->cx == poh->cx) &&
  179. (pohBeside->pohLeft == poh->pohLeft) &&
  180. (pohBeside->pohRight == poh->pohRight) &&
  181. (pohBeside->pohDown->pohUp != pohBeside))
  182. {
  183. poh->cy += pohBeside->cy;
  184. poh->pohDown = pohBeside->pohDown;
  185. pohBeside->pohNext->pohPrev = pohBeside->pohPrev;
  186. pohBeside->pohPrev->pohNext = pohBeside->pohNext;
  187. vOhFreeNode(ppdev, pohBeside);
  188. goto MergeLoop;
  189. }
  190. // Try merging with the left sibling:
  191. pohBeside = poh->pohLeft;
  192. if ((pohBeside->ofl & OFL_AVAILABLE) &&
  193. (pohBeside->cy == poh->cy) &&
  194. (pohBeside->pohUp == poh->pohUp) &&
  195. (pohBeside->pohDown == poh->pohDown) &&
  196. (pohBeside->pohRight == poh) &&
  197. (poh->pohRight->pohLeft != poh))
  198. {
  199. // We add our rectangle to the one to the left:
  200. pohBeside->cx += poh->cx;
  201. pohBeside->pohRight = poh->pohRight;
  202. // Remove 'poh' from the ??? list and free it:
  203. poh->pohNext->pohPrev = poh->pohPrev;
  204. poh->pohPrev->pohNext = poh->pohNext;
  205. vOhFreeNode(ppdev, poh);
  206. poh = pohBeside;
  207. goto MergeLoop;
  208. }
  209. // Try merging with the upper sibling:
  210. pohBeside = poh->pohUp;
  211. if ((pohBeside->ofl & OFL_AVAILABLE) &&
  212. (pohBeside->cx == poh->cx) &&
  213. (pohBeside->pohLeft == poh->pohLeft) &&
  214. (pohBeside->pohRight == poh->pohRight) &&
  215. (pohBeside->pohDown == poh) &&
  216. (poh->pohDown->pohUp != poh))
  217. {
  218. pohBeside->cy += poh->cy;
  219. pohBeside->pohDown = poh->pohDown;
  220. poh->pohNext->pohPrev = poh->pohPrev;
  221. poh->pohPrev->pohNext = poh->pohNext;
  222. vOhFreeNode(ppdev, poh);
  223. poh = pohBeside;
  224. goto MergeLoop;
  225. }
  226. // Remove the node from the ???list if it was in use (we wouldn't
  227. // want to do this for a OFL_PERMANENT node that had been freed):
  228. poh->pohNext->pohPrev = poh->pohPrev;
  229. poh->pohPrev->pohNext = poh->pohNext;
  230. cxcy = CXCY(poh->cx, poh->cy);
  231. // Insert the node into the available list:
  232. pohNext = ppdev->heap.ohAvailable.pohNext;
  233. while (pohNext->cxcy < cxcy)
  234. {
  235. pohNext = pohNext->pohNext;
  236. }
  237. pohPrev = pohNext->pohPrev;
  238. pohPrev->pohNext = poh;
  239. pohNext->pohPrev = poh;
  240. poh->pohPrev = pohPrev;
  241. poh->pohNext = pohNext;
  242. poh->ofl = OFL_AVAILABLE;
  243. poh->cxcy = cxcy;
  244. // Return the node pointer for the new and improved available rectangle:
  245. return(poh);
  246. }
  247. /******************************Public*Routine******************************\
  248. * OH* pohAllocate
  249. *
  250. * Allocates space for an off-screen rectangle. It will attempt to find
  251. * the smallest available free rectangle, and will allocate the block out
  252. * of its upper-left corner. The remaining two rectangles will be placed
  253. * on the available free space list.
  254. *
  255. * If the rectangle would have been large enough to fit into off-screen
  256. * memory, but there is not enough available free space, we will boot
  257. * bitmaps out of off-screen and into DIBs until there is enough room.
  258. *
  259. \**************************************************************************/
  260. OH* pohAllocate(
  261. PDEV* ppdev,
  262. LONG cxThis, // Width of rectangle to be allocated
  263. LONG cyThis, // Height of rectangle to be allocated
  264. FLOH floh) // Allocation flags
  265. {
  266. ULONG cxcyThis; // Width and height search key
  267. OH* pohThis; // Points to found available rectangle we'll use
  268. ULONG cxcy; // Temporary versions
  269. OH* pohNext;
  270. OH* pohPrev;
  271. LONG cxRem;
  272. LONG cyRem;
  273. OH* pohBelow;
  274. LONG cxBelow;
  275. LONG cyBelow;
  276. OH* pohBeside;
  277. LONG cxBeside;
  278. LONG cyBeside;
  279. DISPDBG((1, "Allocating %li x %li...", cxThis, cyThis));
  280. ASSERTDD((cxThis > 0) && (cyThis > 0), "Illegal allocation size");
  281. // Increase the width to get the proper alignment (thus ensuring that all
  282. // allocations will be properly aligned):
  283. cxThis = (cxThis + (HEAP_X_ALIGNMENT - 1)) & ~(HEAP_X_ALIGNMENT - 1);
  284. // We can't succeed if the requested rectangle is larger than the
  285. // largest possible available rectangle:
  286. if ((cxThis > ppdev->heap.cxMax) || (cyThis > ppdev->heap.cyMax))
  287. return(NULL);
  288. // Find the first available rectangle the same size or larger than
  289. // the requested one:
  290. cxcyThis = CXCY(cxThis, cyThis);
  291. pohThis = ppdev->heap.ohAvailable.pohNext;
  292. while (pohThis->cxcy < cxcyThis)
  293. {
  294. pohThis = pohThis->pohNext;
  295. }
  296. while (pohThis->cy < cyThis)
  297. {
  298. pohThis = pohThis->pohNext;
  299. }
  300. if (pohThis->cxcy == CXCY_SENTINEL)
  301. {
  302. // There was no space large enough...
  303. if (floh & FLOH_ONLY_IF_ROOM)
  304. return(NULL);
  305. // We couldn't find an available rectangle that was big enough
  306. // to fit our request. So throw things out of the heap until we
  307. // have room:
  308. do {
  309. pohThis = ppdev->heap.ohDfb.pohPrev; // Least-recently blitted
  310. ASSERTDD(pohThis != &ppdev->heap.ohDfb, "Ran out of in-use entries");
  311. // We can safely exit here if we have to:
  312. pohThis = pohMoveOffscreenDfbToDib(ppdev, pohThis);
  313. if (pohThis == NULL)
  314. return(NULL);
  315. } while ((pohThis->cx < cxThis) || (pohThis->cy < cyThis));
  316. }
  317. // We've now found an available rectangle that is the same size or
  318. // bigger than our requested rectangle. We're going to use the
  319. // upper-left corner of our found rectangle, and divide the unused
  320. // remainder into two rectangles which will go on the available
  321. // list.
  322. // Compute the width of the unused rectangle to the right, and the
  323. // height of the unused rectangle below:
  324. cyRem = pohThis->cy - cyThis;
  325. cxRem = pohThis->cx - cxThis;
  326. // Given finite area, we wish to find the two rectangles that are
  327. // most square -- i.e., the arrangement that gives two rectangles
  328. // with the least perimiter:
  329. cyBelow = cyRem;
  330. cxBeside = cxRem;
  331. if (cxRem <= cyRem)
  332. {
  333. cxBelow = cxThis + cxRem;
  334. cyBeside = cyThis;
  335. }
  336. else
  337. {
  338. cxBelow = cxThis;
  339. cyBeside = cyThis + cyRem;
  340. }
  341. // We only make new available rectangles of the unused right and bottom
  342. // portions if they're greater in dimension than OH_QUANTUM (it hardly
  343. // makes sense to do the book-work to keep around a 2-pixel wide
  344. // available space, for example):
  345. pohBeside = NULL;
  346. if (cxBeside >= OH_QUANTUM)
  347. {
  348. pohBeside = pohNewNode(ppdev);
  349. if (pohBeside == NULL)
  350. return(NULL);
  351. }
  352. pohBelow = NULL;
  353. if (cyBelow >= OH_QUANTUM)
  354. {
  355. pohBelow = pohNewNode(ppdev);
  356. if (pohBelow == NULL)
  357. {
  358. vOhFreeNode(ppdev, pohBeside);
  359. return(NULL);
  360. }
  361. // Insert this rectangle into the available list (which is
  362. // sorted on ascending cxcy):
  363. cxcy = CXCY(cxBelow, cyBelow);
  364. pohNext = ppdev->heap.ohAvailable.pohNext;
  365. while (pohNext->cxcy < cxcy)
  366. {
  367. pohNext = pohNext->pohNext;
  368. }
  369. pohPrev = pohNext->pohPrev;
  370. pohPrev->pohNext = pohBelow;
  371. pohNext->pohPrev = pohBelow;
  372. pohBelow->pohPrev = pohPrev;
  373. pohBelow->pohNext = pohNext;
  374. // Now update the adjacency information:
  375. pohBelow->pohLeft = pohThis->pohLeft;
  376. pohBelow->pohUp = pohThis;
  377. pohBelow->pohRight = pohThis->pohRight;
  378. pohBelow->pohDown = pohThis->pohDown;
  379. // Update the rest of the new node information:
  380. pohBelow->cxcy = cxcy;
  381. pohBelow->ofl = OFL_AVAILABLE;
  382. pohBelow->x = pohThis->x;
  383. pohBelow->y = pohThis->y + cyThis;
  384. pohBelow->cx = cxBelow;
  385. pohBelow->cy = cyBelow;
  386. // Modify the current node to reflect the changes we've made:
  387. pohThis->cy = cyThis;
  388. }
  389. if (cxBeside >= OH_QUANTUM)
  390. {
  391. // Insert this rectangle into the available list (which is
  392. // sorted on ascending cxcy):
  393. cxcy = CXCY(cxBeside, cyBeside);
  394. pohNext = ppdev->heap.ohAvailable.pohNext;
  395. while (pohNext->cxcy < cxcy)
  396. {
  397. pohNext = pohNext->pohNext;
  398. }
  399. pohPrev = pohNext->pohPrev;
  400. pohPrev->pohNext = pohBeside;
  401. pohNext->pohPrev = pohBeside;
  402. pohBeside->pohPrev = pohPrev;
  403. pohBeside->pohNext = pohNext;
  404. // Now update the adjacency information:
  405. pohBeside->pohUp = pohThis->pohUp;
  406. pohBeside->pohLeft = pohThis;
  407. pohBeside->pohDown = pohThis->pohDown;
  408. pohBeside->pohRight = pohThis->pohRight;
  409. // Update the rest of the new node information:
  410. pohBeside->cxcy = cxcy;
  411. pohBeside->ofl = OFL_AVAILABLE;
  412. pohBeside->x = pohThis->x + cxThis;
  413. pohBeside->y = pohThis->y;
  414. pohBeside->cx = cxBeside;
  415. pohBeside->cy = cyBeside;
  416. // Modify the current node to reflect the changes we've made:
  417. pohThis->cx = cxThis;
  418. }
  419. if (pohBelow != NULL)
  420. {
  421. pohThis->pohDown = pohBelow;
  422. if ((pohBeside != NULL) && (cyBeside == pohThis->cy))
  423. pohBeside->pohDown = pohBelow;
  424. }
  425. if (pohBeside != NULL)
  426. {
  427. pohThis->pohRight = pohBeside;
  428. if ((pohBelow != NULL) && (cxBelow == pohThis->cx))
  429. pohBelow->pohRight = pohBeside;
  430. }
  431. pohThis->ofl = OFL_INUSE;
  432. pohThis->cxcy = CXCY(pohThis->cx, pohThis->cy);
  433. pohThis->pdsurf = NULL; // Caller is responsible for
  434. // setting this field
  435. // Remove this from the available list:
  436. pohThis->pohPrev->pohNext = pohThis->pohNext;
  437. pohThis->pohNext->pohPrev = pohThis->pohPrev;
  438. // Now insert this at the head of the DFB list:
  439. pohThis->pohNext = ppdev->heap.ohDfb.pohNext;
  440. pohThis->pohPrev = &ppdev->heap.ohDfb;
  441. ppdev->heap.ohDfb.pohNext->pohPrev = pohThis;
  442. ppdev->heap.ohDfb.pohNext = pohThis;
  443. DISPDBG((1, " Allocated at (%li, %li)", pohThis->x, pohThis->y));
  444. // Calculate the effective start address for this bitmap in off-
  445. // screen memory:
  446. pohThis->pvScan0 = ppdev->pjScreen + (pohThis->y * ppdev->lDelta)
  447. + (pohThis->x * ppdev->cjPel);
  448. return(pohThis);
  449. }
  450. /******************************Public*Routine******************************\
  451. * VOID vCalculateMaxmimum
  452. *
  453. * Traverses the list of in-use and available rectangles to find the one
  454. * with the maximal area.
  455. *
  456. \**************************************************************************/
  457. VOID vCalculateMaximum(
  458. PDEV* ppdev)
  459. {
  460. OH* poh;
  461. OH* pohSentinel;
  462. LONG lArea;
  463. LONG lMaxArea;
  464. LONG cxMax;
  465. LONG cyMax;
  466. LONG i;
  467. lMaxArea = 0;
  468. cxMax = 0;
  469. cyMax = 0;
  470. // First time through, loop through the list of available rectangles:
  471. pohSentinel = &ppdev->heap.ohAvailable;
  472. for (i = 2; i != 0; i--)
  473. {
  474. for (poh = pohSentinel->pohNext; poh != pohSentinel; poh = poh->pohNext)
  475. {
  476. ASSERTDD(!(poh->ofl & OFL_PERMANENT),
  477. "Permanent in available/DFB chain?");
  478. // We don't have worry about this multiply overflowing
  479. // because we are dealing in physical screen coordinates,
  480. // which will probably never be more than 15 bits:
  481. lArea = poh->cx * poh->cy;
  482. if (lArea > lMaxArea)
  483. {
  484. cxMax = poh->cx;
  485. cyMax = poh->cy;
  486. lMaxArea = lArea;
  487. }
  488. }
  489. // Second time through, loop through the list of in-use rectangles:
  490. pohSentinel = &ppdev->heap.ohDfb;
  491. }
  492. // All that we are interested in is the dimensions of the rectangle
  493. // that has the largest possible available area (and remember that
  494. // there might not be any possible available area):
  495. ppdev->heap.cxMax = cxMax;
  496. ppdev->heap.cyMax = cyMax;
  497. }
  498. /******************************Public*Routine******************************\
  499. * OH* pohAllocatePermanent
  500. *
  501. * Allocates an off-screen rectangle that can never be booted of the heap.
  502. * It's the caller's responsibility to manage the rectangle, which includes
  503. * what to do with the memory in DrvAssertMode when the display is changed
  504. * to full-screen mode.
  505. *
  506. \**************************************************************************/
  507. OH* pohAllocatePermanent(
  508. PDEV* ppdev,
  509. LONG cx,
  510. LONG cy)
  511. {
  512. OH* poh;
  513. poh = pohAllocate(ppdev, cx, cy, 0);
  514. if (poh != NULL)
  515. {
  516. // Mark the rectangle as permanent:
  517. poh->ofl = OFL_PERMANENT;
  518. // Remove the node from the most-recently blitted list:
  519. poh->pohPrev->pohNext = poh->pohNext;
  520. poh->pohNext->pohPrev = poh->pohPrev;
  521. poh->pohPrev = NULL;
  522. poh->pohNext = NULL;
  523. // Now calculate the new maximum size rectangle available in the
  524. // heap:
  525. vCalculateMaximum(ppdev);
  526. }
  527. return(poh);
  528. }
  529. /******************************Public*Routine******************************\
  530. * BOOL bMoveDibToOffscreenDfbIfRoom
  531. *
  532. * Converts the DIB DFB to an off-screen DFB, if there's room for it in
  533. * off-screen memory.
  534. *
  535. * Returns: FALSE if there wasn't room, TRUE if successfully moved.
  536. *
  537. \**************************************************************************/
  538. BOOL bMoveDibToOffscreenDfbIfRoom(
  539. PDEV* ppdev,
  540. DSURF* pdsurf)
  541. {
  542. OH* poh;
  543. SURFOBJ* pso;
  544. RECTL rclDst;
  545. POINTL ptlSrc;
  546. HSURF hsurf;
  547. ASSERTDD(pdsurf->dt == DT_DIB,
  548. "Can't move a bitmap off-screen when it's already off-screen");
  549. // If we're in full-screen mode, we can't move anything to off-screen
  550. // memory:
  551. if (!ppdev->bEnabled)
  552. return(FALSE);
  553. poh = pohAllocate(ppdev, pdsurf->sizl.cx, pdsurf->sizl.cy,
  554. FLOH_ONLY_IF_ROOM);
  555. if (poh == NULL)
  556. {
  557. // There wasn't any free room.
  558. return(FALSE);
  559. }
  560. // 'pdsurf->sizl' is the actual bitmap dimension, not 'poh->cx' or
  561. // 'poh->cy'.
  562. rclDst.left = poh->x;
  563. rclDst.top = poh->y;
  564. rclDst.right = rclDst.left + pdsurf->sizl.cx;
  565. rclDst.bottom = rclDst.top + pdsurf->sizl.cy;
  566. ptlSrc.x = 0;
  567. ptlSrc.y = 0;
  568. vPutBits(ppdev, pdsurf->pso, &rclDst, &ptlSrc);
  569. // Update the data structures to reflect the new off-screen node:
  570. pso = pdsurf->pso;
  571. pdsurf->dt = DT_SCREEN;
  572. pdsurf->poh = poh;
  573. poh->pdsurf = pdsurf;
  574. // Now free the DIB. Get the hsurf from the SURFOBJ before we unlock
  575. // it (it's not legal to dereference psoDib when it's unlocked):
  576. hsurf = pso->hsurf;
  577. EngUnlockSurface(pso);
  578. EngDeleteSurface(hsurf);
  579. return(TRUE);
  580. }
  581. /******************************Public*Routine******************************\
  582. * OH* pohMoveOffscreenDfbToDib
  583. *
  584. * Converts the DFB from being off-screen to being a DIB.
  585. *
  586. * Note: The caller does NOT have to call 'pohFree' on 'poh' after making
  587. * this call.
  588. *
  589. * Returns: NULL if the function failed (due to a memory allocation).
  590. * Otherwise, it returns a pointer to the coalesced off-screen heap
  591. * node that has been made available for subsequent allocations
  592. * (useful when trying to free enough memory to make a new
  593. * allocation).
  594. \**************************************************************************/
  595. OH* pohMoveOffscreenDfbToDib(
  596. PDEV* ppdev,
  597. OH* poh)
  598. {
  599. DSURF* pdsurf;
  600. HBITMAP hbmDib;
  601. SURFOBJ* pso;
  602. RECTL rclDst;
  603. POINTL ptlSrc;
  604. DISPDBG((1, "Throwing out %li x %li at (%li, %li)!",
  605. poh->cx, poh->cy, poh->x, poh->y));
  606. pdsurf = poh->pdsurf;
  607. ASSERTDD((poh->x != 0) || (poh->y != 0),
  608. "Can't make the visible screen into a DIB");
  609. ASSERTDD(pdsurf->dt != DT_DIB,
  610. "Can't make a DIB into even more of a DIB");
  611. hbmDib = EngCreateBitmap(pdsurf->sizl, 0, ppdev->iBitmapFormat,
  612. BMF_TOPDOWN, NULL);
  613. if (hbmDib)
  614. {
  615. if (EngAssociateSurface((HSURF) hbmDib, ppdev->hdevEng, 0))
  616. {
  617. pso = EngLockSurface((HSURF) hbmDib);
  618. if (pso != NULL)
  619. {
  620. rclDst.left = 0;
  621. rclDst.top = 0;
  622. rclDst.right = pdsurf->sizl.cx;
  623. rclDst.bottom = pdsurf->sizl.cy;
  624. ptlSrc.x = poh->x;
  625. ptlSrc.y = poh->y;
  626. vGetBits(ppdev, pso, &rclDst, &ptlSrc);
  627. pdsurf->dt = DT_DIB;
  628. pdsurf->pso = pso;
  629. // Don't even bother checking to see if this DIB should
  630. // be put back into off-screen memory until the next
  631. // heap 'free' occurs:
  632. pdsurf->iUniq = ppdev->iHeapUniq;
  633. pdsurf->cBlt = 0;
  634. // Remove this node from the off-screen DFB list, and free
  635. // it. 'pohFree' will never return NULL:
  636. return(pohFree(ppdev, poh));
  637. }
  638. }
  639. // Fail case:
  640. EngDeleteSurface((HSURF) hbmDib);
  641. }
  642. return(NULL);
  643. }
  644. /******************************Public*Routine******************************\
  645. * BOOL bMoveEverythingFromOffscreenToDibs
  646. *
  647. * This function is used when we're about to enter full-screen mode, which
  648. * would wipe all our off-screen bitmaps. GDI can ask us to draw on
  649. * device bitmaps even when we're in full-screen mode, and we do NOT have
  650. * the option of stalling the call until we switch out of full-screen.
  651. * We have no choice but to move all the off-screen DFBs to DIBs.
  652. *
  653. * Returns TRUE if all DSURFs have been successfully moved.
  654. *
  655. \**************************************************************************/
  656. BOOL bMoveAllDfbsFromOffscreenToDibs(
  657. PDEV* ppdev)
  658. {
  659. OH* poh;
  660. OH* pohNext;
  661. BOOL bRet;
  662. bRet = TRUE;
  663. poh = ppdev->heap.ohDfb.pohNext;
  664. while (poh != &ppdev->heap.ohDfb)
  665. {
  666. pohNext = poh->pohNext;
  667. // If something's already a DIB, we shouldn't try to make it even
  668. // more of a DIB:
  669. if (poh->pdsurf->dt == DT_SCREEN)
  670. {
  671. if (!pohMoveOffscreenDfbToDib(ppdev, poh))
  672. bRet = FALSE;
  673. }
  674. poh = pohNext;
  675. }
  676. return(bRet);
  677. }
  678. /******************************Public*Routine******************************\
  679. * HBITMAP DrvCreateDeviceBitmap
  680. *
  681. * Function called by GDI to create a device-format-bitmap (DFB). We will
  682. * always try to allocate the bitmap in off-screen; if we can't, we simply
  683. * fail the call and GDI will create and manage the bitmap itself.
  684. *
  685. * Note: We do not have to zero the bitmap bits. GDI will automatically
  686. * call us via DrvBitBlt to zero the bits (which is a security
  687. * consideration).
  688. *
  689. \**************************************************************************/
  690. HBITMAP DrvCreateDeviceBitmap(
  691. DHPDEV dhpdev,
  692. SIZEL sizl,
  693. ULONG iFormat)
  694. {
  695. PDEV* ppdev;
  696. OH* poh;
  697. DSURF* pdsurf;
  698. HBITMAP hbmDevice;
  699. FLONG flHooks;
  700. // 8 bit stretch blts fail for unknown reasons on ppc, so preclude
  701. // accelerated offscreen bitmaps.
  702. #ifdef PPC
  703. return (0);
  704. #endif
  705. ppdev = (PDEV*) dhpdev;
  706. // We don't bother supporting device bitmaps with the P9000 when at
  707. // 16bpp or 32bpp:
  708. if (ppdev->flStat & STAT_UNACCELERATED)
  709. return(0);
  710. // If we're in full-screen mode, we hardly have any off-screen memory
  711. // in which to allocate a DFB. LATER: We could still allocate an
  712. // OH node and put the bitmap on the DIB DFB list for later promotion.
  713. if (!ppdev->bEnabled)
  714. return(0);
  715. // We only support device bitmaps that are the same colour depth
  716. // as our display.
  717. //
  718. // Actually, those are the only kind GDI will ever call us with,
  719. // but we may as well check. Note that this implies you'll never
  720. // get a crack at 1bpp bitmaps.
  721. if (iFormat != ppdev->iBitmapFormat)
  722. return(0);
  723. // We don't want anything 8x8 or smaller -- they're typically brush
  724. // patterns which we don't particularly want to stash in off-screen
  725. // memory:
  726. if ((sizl.cx <= 8) && (sizl.cy <= 8))
  727. return(0);
  728. poh = pohAllocate(ppdev, sizl.cx, sizl.cy, 0);
  729. if (poh != NULL)
  730. {
  731. pdsurf = EngAllocMem(0, sizeof(DSURF), ALLOC_TAG);
  732. if (pdsurf != NULL)
  733. {
  734. hbmDevice = EngCreateDeviceBitmap((DHSURF) pdsurf, sizl, iFormat);
  735. if (hbmDevice != NULL)
  736. {
  737. flHooks = ppdev->flHooks;
  738. // Setting the SYNCHRONIZEACCESS flag tells GDI that we
  739. // want all drawing to the bitmaps to be synchronized (GDI
  740. // is multi-threaded and by default does not synchronize
  741. // device bitmap drawing -- it would be a Bad Thing for us
  742. // to have multiple threads using the accelerator at the
  743. // same time):
  744. flHooks |= HOOK_SYNCHRONIZEACCESS;
  745. // It's a device-managed surface; make sure we don't set
  746. // HOOK_SYNCHRONIZE, otherwise we may confuse GDI:
  747. flHooks &= ~HOOK_SYNCHRONIZE;
  748. if (EngAssociateSurface((HSURF) hbmDevice, ppdev->hdevEng,
  749. flHooks))
  750. {
  751. pdsurf->dt = DT_SCREEN;
  752. pdsurf->poh = poh;
  753. pdsurf->sizl = sizl;
  754. pdsurf->ppdev = ppdev;
  755. poh->pdsurf = pdsurf;
  756. return(hbmDevice);
  757. }
  758. EngDeleteSurface((HSURF) hbmDevice);
  759. }
  760. EngFreeMem(pdsurf);
  761. }
  762. pohFree(ppdev, poh);
  763. }
  764. return(0);
  765. }
  766. /******************************Public*Routine******************************\
  767. * VOID DrvDeleteDeviceBitmap
  768. *
  769. * Deletes a DFB.
  770. *
  771. \**************************************************************************/
  772. VOID DrvDeleteDeviceBitmap(
  773. DHSURF dhsurf)
  774. {
  775. DSURF* pdsurf;
  776. PDEV* ppdev;
  777. SURFOBJ* psoDib;
  778. HSURF hsurfDib;
  779. pdsurf = (DSURF*) dhsurf;
  780. ppdev = pdsurf->ppdev;
  781. if (pdsurf->dt == DT_SCREEN)
  782. {
  783. pohFree(ppdev, pdsurf->poh);
  784. }
  785. else
  786. {
  787. ASSERTDD(pdsurf->dt == DT_DIB, "Expected DIB type");
  788. psoDib = pdsurf->pso;
  789. // Get the hsurf from the SURFOBJ before we unlock it (it's not
  790. // legal to dereference psoDib when it's unlocked):
  791. hsurfDib = psoDib->hsurf;
  792. EngUnlockSurface(psoDib);
  793. EngDeleteSurface(hsurfDib);
  794. }
  795. EngFreeMem(pdsurf);
  796. }
  797. /******************************Public*Routine******************************\
  798. * BOOL bAssertModeOffscreenHeap
  799. *
  800. * This function is called whenever we switch in or out of full-screen
  801. * mode. We have to convert all the off-screen bitmaps to DIBs when
  802. * we switch to full-screen (because we may be asked to draw on them even
  803. * when in full-screen, and the mode switch would probably nuke the video
  804. * memory contents anyway).
  805. *
  806. \**************************************************************************/
  807. BOOL bAssertModeOffscreenHeap(
  808. PDEV* ppdev,
  809. BOOL bEnable)
  810. {
  811. BOOL b;
  812. b = TRUE;
  813. if (!bEnable)
  814. {
  815. b = bMoveAllDfbsFromOffscreenToDibs(ppdev);
  816. }
  817. return(b);
  818. }
  819. /******************************Public*Routine******************************\
  820. * VOID vDisableOffscreenHeap
  821. *
  822. * Frees any resources allocated by the off-screen heap.
  823. *
  824. \**************************************************************************/
  825. VOID vDisableOffscreenHeap(
  826. PDEV* ppdev)
  827. {
  828. OHALLOC* poha;
  829. OHALLOC* pohaNext;
  830. SURFOBJ* psoPunt;
  831. HSURF hsurf;
  832. psoPunt = ppdev->psoPunt;
  833. if (psoPunt != NULL)
  834. {
  835. hsurf = psoPunt->hsurf;
  836. EngUnlockSurface(psoPunt);
  837. EngDeleteSurface(hsurf);
  838. }
  839. psoPunt = ppdev->psoPunt2;
  840. if (psoPunt != NULL)
  841. {
  842. hsurf = psoPunt->hsurf;
  843. EngUnlockSurface(psoPunt);
  844. EngDeleteSurface(hsurf);
  845. }
  846. poha = ppdev->heap.pohaChain;
  847. while (poha != NULL)
  848. {
  849. pohaNext = poha->pohaNext; // Grab the next pointer before it's freed
  850. EngFreeMem(poha);
  851. poha = pohaNext;
  852. }
  853. }
  854. /******************************Public*Routine******************************\
  855. * BOOL bEnableOffscreenHeap
  856. *
  857. * Initializes the off-screen heap using all available video memory,
  858. * accounting for the portion taken by the visible screen.
  859. *
  860. * Input: ppdev->cxScreen
  861. * ppdev->cyScreen
  862. * ppdev->cxMemory
  863. * ppdev->cyMemory
  864. *
  865. \**************************************************************************/
  866. BOOL bEnableOffscreenHeap(
  867. PDEV* ppdev)
  868. {
  869. OH* poh;
  870. SIZEL sizl;
  871. HSURF hsurf;
  872. DISPDBG((5, "Screen: %li x %li Memory: %li x %li",
  873. ppdev->cxScreen, ppdev->cyScreen, ppdev->cxMemory, ppdev->cyMemory));
  874. ppdev->heap.pohaChain = NULL;
  875. ppdev->heap.pohFreeList = NULL;
  876. // Initialize the available list, which will be a circular
  877. // doubly-linked list kept in ascending 'cxcy' order, with a
  878. // 'sentinel' at the end of the list:
  879. poh = pohNewNode(ppdev);
  880. if (poh == NULL)
  881. goto ReturnFalse;
  882. // The first node describes the entire video memory size:
  883. poh->pohNext = &ppdev->heap.ohAvailable;
  884. poh->pohPrev = &ppdev->heap.ohAvailable;
  885. poh->ofl = OFL_AVAILABLE;
  886. poh->x = 0;
  887. poh->y = 0;
  888. poh->cx = ppdev->cxMemory;
  889. poh->cy = ppdev->cyMemory;
  890. poh->cxcy = CXCY(ppdev->cxMemory, ppdev->cyMemory);
  891. poh->pohLeft = &ppdev->heap.ohAvailable;
  892. poh->pohUp = &ppdev->heap.ohAvailable;
  893. poh->pohRight = &ppdev->heap.ohAvailable;
  894. poh->pohDown = &ppdev->heap.ohAvailable;
  895. poh->pvScan0 = ppdev->pjScreen;
  896. // The second node is our available list sentinel:
  897. ppdev->heap.ohAvailable.pohNext = poh;
  898. ppdev->heap.ohAvailable.pohPrev = poh;
  899. ppdev->heap.ohAvailable.cxcy = CXCY_SENTINEL;
  900. ppdev->heap.ohAvailable.cx = 0x7fffffff;
  901. ppdev->heap.ohAvailable.cy = 0x7fffffff;
  902. ppdev->heap.ohAvailable.ofl = OFL_PERMANENT;
  903. ppdev->heap.ohDfb.pohLeft = NULL;
  904. ppdev->heap.ohDfb.pohUp = NULL;
  905. ppdev->heap.ohDfb.pohRight = NULL;
  906. ppdev->heap.ohDfb.pohDown = NULL;
  907. // Initialize the most-recently-blitted DFB list, which will be
  908. // a circular doubly-linked list kept in order, with a sentinel at
  909. // the end. This node is also used for the screen-surface, for its
  910. // offset:
  911. ppdev->heap.ohDfb.pohNext = &ppdev->heap.ohDfb;
  912. ppdev->heap.ohDfb.pohPrev = &ppdev->heap.ohDfb;
  913. ppdev->heap.ohDfb.ofl = OFL_PERMANENT;
  914. // For the moment, make the max really big so that the first
  915. // allocation we're about to do will succeed:
  916. ppdev->heap.cxMax = 0x7fffffff;
  917. ppdev->heap.cyMax = 0x7fffffff;
  918. // Finally, reserve the upper-left corner for the screen. We can
  919. // actually throw away 'poh' because we'll never need it again
  920. // (not even for disabling the off-screen heap since everything is
  921. // freed using OHALLOCs):
  922. poh = pohAllocatePermanent(ppdev, ppdev->cxScreen, ppdev->cyScreen);
  923. // Remember it so that we can associate the screen SURFOBJ with this
  924. // poh:
  925. ppdev->pohScreen = poh;
  926. ASSERTDD((poh != NULL) && (poh->x == 0) && (poh->y == 0),
  927. "We assumed allocator would use the upper-left corner");
  928. // Allocate a 'punt' SURFOBJ we'll use when the device-bitmap is in
  929. // off-screen memory, but we want GDI to draw to it directly as an
  930. // engine-managed surface:
  931. sizl.cx = ppdev->cxMemory;
  932. sizl.cy = ppdev->cyMemory;
  933. // We want to create it with exactly the same hooks, capabilities,
  934. // and screen delta as our primary surface:
  935. hsurf = (HSURF) EngCreateBitmap(sizl,
  936. ppdev->lDelta,
  937. ppdev->iBitmapFormat,
  938. BMF_TOPDOWN,
  939. ppdev->pjScreen);
  940. if ((hsurf == 0) ||
  941. (!EngAssociateSurface(hsurf, ppdev->hdevEng, ppdev->flHooks)) ||
  942. (!(ppdev->psoPunt = EngLockSurface(hsurf))))
  943. {
  944. DISPDBG((0, "Failed punt surface creation"));
  945. EngDeleteSurface(hsurf);
  946. goto ReturnFalse;
  947. }
  948. // We need another for doing DrvBitBlt and DrvCopyBits when both
  949. // surfaces are off-screen bitmaps:
  950. hsurf = (HSURF) EngCreateBitmap(sizl,
  951. ppdev->lDelta,
  952. ppdev->iBitmapFormat,
  953. BMF_TOPDOWN,
  954. ppdev->pjScreen);
  955. if ((hsurf == 0) ||
  956. (!EngAssociateSurface(hsurf, ppdev->hdevEng, ppdev->flHooks)) ||
  957. (!(ppdev->psoPunt2 = EngLockSurface(hsurf))))
  958. {
  959. DISPDBG((0, "Failed punt surface creation"));
  960. EngDeleteSurface(hsurf);
  961. goto ReturnFalse;
  962. }
  963. DISPDBG((5, "Passed bEnableOffscreenHeap"));
  964. if (poh != NULL)
  965. return(TRUE);
  966. ReturnFalse:
  967. DISPDBG((0, "Failed bEnableOffscreenHeap"));
  968. return(FALSE);
  969. }