Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

676 lines
23 KiB

  1. /******************************Module*Header*******************************\
  2. *
  3. * *******************
  4. * * GDI SAMPLE CODE *
  5. * *******************
  6. *
  7. * Module Name: heap.c
  8. *
  9. * This module contains the routines for an off-screen video heap manager.
  10. * It is used primarily for allocating space for device-format-bitmaps in
  11. * off-screen memory.
  12. *
  13. * Off-screen bitmaps are a big deal on NT because:
  14. *
  15. * 1) It reduces the working set. Any bitmap stored in off-screen
  16. * memory is a bitmap that isn't taking up space in main memory.
  17. *
  18. * 2) There is a speed win by using the accelerator hardware for
  19. * drawing, in place of NT's GDI code. NT's GDI is written entirely
  20. * in 'C++' and perhaps isn't as fast as it could be.
  21. *
  22. * 3) It raises your Winbench score.
  23. *
  24. * Copyright (c) 1993-1998 Microsoft Corporation
  25. \**************************************************************************/
  26. #include "precomp.h"
  27. /******************************Public*Routine******************************\
  28. * DSURF* pVidMemAllocate
  29. *
  30. \**************************************************************************/
  31. DSURF* pVidMemAllocate(
  32. PDEV* ppdev,
  33. LONG cx,
  34. LONG cy)
  35. {
  36. ULONG iHeap;
  37. VIDEOMEMORY* pvmHeap;
  38. FLATPTR fpVidMem;
  39. DSURF* pdsurf;
  40. LONG lDelta;
  41. SURFACEALIGNMENT Alignment;
  42. memset(&Alignment, 0, sizeof(Alignment));
  43. // Ensure quadword x-alignment in video memory:
  44. Alignment.Rectangular.dwXAlignment = 8;
  45. Alignment.Rectangular.dwFlags |= SURFACEALIGN_DISCARDABLE;
  46. for (iHeap = 0; iHeap < ppdev->cHeaps; iHeap++)
  47. {
  48. pvmHeap = &ppdev->pvmList[iHeap];
  49. // AGP memory could be potentially used for device-bitmaps, with
  50. // two very large caveats:
  51. //
  52. // 1. No kernel-mode view is made for the AGP memory (would take
  53. // up too many PTEs and too much virtual address space).
  54. // No user-mode view is made either unless a DirectDraw
  55. // application happens to be running. Consequently, neither
  56. // GDI nor the driver can use the CPU to directly access the
  57. // bits. (It can be done through the accelerator, however.)
  58. //
  59. // 2. AGP heaps never shrink their committed allocations. The
  60. // only time AGP memory gets de-committed is when the entire
  61. // heap is empty. And don't forget that committed AGP memory
  62. // is non-pageable. Consequently, if you were to enable a
  63. // 50 MB AGP heap for DirectDraw, and were sharing that heap
  64. // for device bitmap allocations, after running a D3D game
  65. // the system would never be able to free that 50 MB of non-
  66. // pageable memory until every single device bitmap was deleted!
  67. // Just watch your Winstone scores plummet if someone plays
  68. // a D3D game first.
  69. if (!(pvmHeap->dwFlags & VIDMEM_ISNONLOCAL))
  70. {
  71. fpVidMem = HeapVidMemAllocAligned(pvmHeap,
  72. cx * ppdev->cjPelSize,
  73. cy,
  74. &Alignment,
  75. &lDelta);
  76. if (fpVidMem != 0)
  77. {
  78. pdsurf = EngAllocMem(FL_ZERO_MEMORY, sizeof(DSURF), ALLOC_TAG);
  79. if (pdsurf != NULL)
  80. {
  81. pdsurf->dt = 0;
  82. pdsurf->ppdev = ppdev;
  83. pdsurf->x = (LONG)(fpVidMem % ppdev->lDelta)
  84. / ppdev->cjPelSize;
  85. pdsurf->y = (LONG)(fpVidMem / ppdev->lDelta);
  86. pdsurf->cx = cx;
  87. pdsurf->cy = cy;
  88. pdsurf->fpVidMem = fpVidMem;
  89. pdsurf->pvmHeap = pvmHeap;
  90. return(pdsurf);
  91. }
  92. VidMemFree(pvmHeap->lpHeap, fpVidMem);
  93. }
  94. }
  95. }
  96. return(NULL);
  97. }
  98. /******************************Public*Routine******************************\
  99. * VOID vVidMemFree
  100. *
  101. \**************************************************************************/
  102. VOID vVidMemFree(
  103. DSURF* pdsurf)
  104. {
  105. DSURF* pTmp;
  106. if (pdsurf == NULL)
  107. return;
  108. if (!(pdsurf->dt & DT_DIRECTDRAW))
  109. {
  110. if (pdsurf->dt & DT_DIB)
  111. {
  112. EngFreeMem(pdsurf->pvScan0);
  113. }
  114. else
  115. {
  116. // Update the uniqueness to show that space has been freed, so
  117. // that we may decide to see if some DIBs can be moved back into
  118. // off-screen memory:
  119. pdsurf->ppdev->iHeapUniq++;
  120. VidMemFree(pdsurf->pvmHeap->lpHeap, pdsurf->fpVidMem);
  121. }
  122. }
  123. EngFreeMem(pdsurf);
  124. }
  125. /******************************Public*Routine******************************\
  126. * BOOL bMoveOldestOffscreenDfbToDib
  127. *
  128. \**************************************************************************/
  129. BOOL bMoveOldestOffscreenDfbToDib(
  130. PDEV* ppdev)
  131. {
  132. DSURF* pdsurf;
  133. LONG lDelta;
  134. VOID* pvScan0;
  135. RECTL rclDst;
  136. POINTL ptlSrc;
  137. SURFOBJ soTmp;
  138. pdsurf = ppdev->pdsurfDiscardableList;
  139. if (pdsurf != NULL)
  140. {
  141. // Make the system-memory scans quadword aligned:
  142. lDelta = (pdsurf->cx * ppdev->cjPelSize + 7) & ~7;
  143. // Note that there's no point in zero-initializing this memory:
  144. pvScan0 = EngAllocMem(0, lDelta * pdsurf->cy, ALLOC_TAG);
  145. if (pvScan0 != NULL)
  146. {
  147. // The following 'EngModifySurface' call tells GDI to
  148. // modify the surface to point to system-memory for
  149. // the bits, and changes what Drv calls we want to
  150. // hook for the surface.
  151. //
  152. // By specifying the surface address, GDI will convert the
  153. // surface to an STYPE_BITMAP surface (if necessary) and
  154. // point the bits to the memory we just allocated. The
  155. // next time we see it in a DrvBitBlt call, the 'dhsurf'
  156. // field will still point to our 'pdsurf' structure.
  157. //
  158. // Note that we hook only CopyBits and BitBlt when we
  159. // convert the device-bitmap to a system-memory surface.
  160. // This is so that we don't have to worry about getting
  161. // DrvTextOut, DrvLineTo, etc. calls on bitmaps that
  162. // we've converted to system-memory -- GDI will just
  163. // automatically do the drawing for us.
  164. //
  165. // However, we are still interested in seeing DrvCopyBits
  166. // and DrvBitBlt calls involving this surface, because
  167. // in those calls we take the opportunity to see if it's
  168. // worth putting the device-bitmap back into video memory
  169. // (if some room has been freed up).
  170. if (EngModifySurface(pdsurf->hsurf,
  171. ppdev->hdevEng,
  172. HOOK_COPYBITS | HOOK_BITBLT,
  173. 0, // It's system-memory
  174. (DHSURF) pdsurf,
  175. pvScan0,
  176. lDelta,
  177. NULL))
  178. {
  179. // First, copy the bits from off-screen memory to the DIB:
  180. rclDst.left = 0;
  181. rclDst.top = 0;
  182. rclDst.right = pdsurf->cx;
  183. rclDst.bottom = pdsurf->cy;
  184. ptlSrc.x = pdsurf->x;
  185. ptlSrc.y = pdsurf->y;
  186. soTmp.lDelta = lDelta;
  187. soTmp.pvScan0 = pvScan0;
  188. vGetBits(ppdev, &soTmp, &rclDst, &ptlSrc);
  189. // Now free the off-screen memory:
  190. VidMemFree(pdsurf->pvmHeap->lpHeap, pdsurf->fpVidMem);
  191. // Remove this node from the discardable list:
  192. ASSERTDD(ppdev->pdsurfDiscardableList == pdsurf,
  193. "Expected node to be head of the list");
  194. ppdev->pdsurfDiscardableList = pdsurf->pdsurfDiscardableNext;
  195. pdsurf->pdsurfDiscardableNext = NULL;
  196. pdsurf->dt = DT_DIB;
  197. pdsurf->pvScan0 = pvScan0;
  198. return(TRUE);
  199. }
  200. EngFreeMem(pvScan0);
  201. }
  202. }
  203. return(FALSE);
  204. }
  205. /******************************Public*Routine******************************\
  206. * BOOL bMoveEverythingFromOffscreenToDibs
  207. *
  208. * This function is used when we're about to enter full-screen mode, which
  209. * would wipe all our off-screen bitmaps. GDI can ask us to draw on
  210. * device bitmaps even when we're in full-screen mode, and we do NOT have
  211. * the option of stalling the call until we switch out of full-screen.
  212. * We have no choice but to move all the off-screen DFBs to DIBs.
  213. *
  214. * Returns TRUE if all DSURFs have been successfully moved.
  215. *
  216. \**************************************************************************/
  217. BOOL bMoveAllDfbsFromOffscreenToDibs(
  218. PDEV* ppdev)
  219. {
  220. do {} while (bMoveOldestOffscreenDfbToDib(ppdev));
  221. return(ppdev->pdsurfDiscardableList == NULL);
  222. }
  223. /******************************Public*Routine******************************\
  224. * BOOL bMoveDibToOffscreenDfbIfRoom
  225. *
  226. \**************************************************************************/
  227. BOOL bMoveDibToOffscreenDfbIfRoom(
  228. PDEV* ppdev,
  229. DSURF* psurf)
  230. {
  231. return(FALSE);
  232. }
  233. /******************************Public*Routine******************************\
  234. * HBITMAP DrvCreateDeviceBitmap
  235. *
  236. * Function called by GDI to create a device-format-bitmap (DFB). We will
  237. * always try to allocate the bitmap in off-screen; if we can't, we simply
  238. * fail the call and GDI will create and manage the bitmap itself.
  239. *
  240. * Note: We do not have to zero the bitmap bits. GDI will automatically
  241. * call us via DrvBitBlt to zero the bits (which is a security
  242. * consideration).
  243. *
  244. \**************************************************************************/
  245. HBITMAP DrvCreateDeviceBitmap(
  246. DHPDEV dhpdev,
  247. SIZEL sizl,
  248. ULONG iFormat)
  249. {
  250. PDEV* ppdev;
  251. DSURF* pdsurf;
  252. HBITMAP hbmDevice;
  253. BYTE* pjSurface;
  254. LONG lDelta;
  255. FLONG flHooks;
  256. DSURF* pTmp;
  257. ppdev = (PDEV*) dhpdev;
  258. // If we're in full-screen mode, we hardly have any off-screen memory
  259. // in which to allocate a DFB.
  260. if (!ppdev->bEnabled)
  261. return(0);
  262. // We only support device bitmaps that are the same colour depth
  263. // as our display.
  264. //
  265. // Actually, those are the only kind GDI will ever call us with,
  266. // but we may as well check. Note that this implies you'll never
  267. // get a crack at 1bpp bitmaps.
  268. // Note: we can't create a device bitmap when the color depth is 24
  269. // BPP. Otherwise, we will have problem in vBankStart when we hack the
  270. // pbnk->pso->pvScan0 = ppdev->pjScreen - cjOffset
  271. // + yOffset * ppdev->lDelta
  272. // + CONVERT_TO_BYTES(xOffset, ppdev);
  273. // this pvScan0 is not guaranteed be DWORD aligned
  274. if ( (iFormat != ppdev->iBitmapFormat)
  275. ||(iFormat == BMF_24BPP) )
  276. return(0);
  277. // We don't want anything 8x8 or smaller -- they're typically brush
  278. // patterns which we don't particularly want to stash in off-screen
  279. // memory.
  280. //
  281. // Note if you're tempted to extend this philosophy to surfaces
  282. // larger than 8x8: in NT5, software cursors will use device-bitmaps
  283. // when possible, which is a big win when they're in video-memory
  284. // because we avoid the horrendous reads from video memory whenever
  285. // the cursor has to be redrawn. But the problem is that these
  286. // are small! (Typically 16x16 to 32x32.)
  287. if ((sizl.cx <= 8) && (sizl.cy <= 8))
  288. return(0);
  289. do {
  290. pdsurf = pVidMemAllocate(ppdev, sizl.cx, sizl.cy);
  291. if (pdsurf != NULL)
  292. {
  293. hbmDevice = EngCreateDeviceBitmap((DHSURF) pdsurf, sizl, iFormat);
  294. if (hbmDevice != NULL)
  295. {
  296. // If we're running on a card that can map all of off-screen
  297. // video-memory, give a pointer to the bits to GDI so that
  298. // it can draw directly on the bits when it wants to.
  299. //
  300. // Note that this requires that we hook DrvSynchronize and
  301. // set HOOK_SYNCHRONIZE.
  302. if ((ppdev->flCaps & CAPS_NEW_MMIO) &&
  303. !(ppdev->flCaps & CAPS_NO_DIRECT_ACCESS))
  304. {
  305. pjSurface = pdsurf->fpVidMem + ppdev->pjScreen;
  306. lDelta = ppdev->lDelta;
  307. flHooks = ppdev->flHooks | HOOK_SYNCHRONIZE;
  308. }
  309. else
  310. {
  311. pjSurface = NULL;
  312. lDelta = 0;
  313. flHooks = ppdev->flHooks;
  314. }
  315. if (EngModifySurface((HSURF) hbmDevice,
  316. ppdev->hdevEng,
  317. flHooks,
  318. MS_NOTSYSTEMMEMORY, // It's video-memory
  319. (DHSURF) pdsurf,
  320. pjSurface,
  321. lDelta,
  322. NULL))
  323. {
  324. pdsurf->hsurf = (HSURF) hbmDevice;
  325. // Add this to the tail of the discardable surface list:
  326. if (ppdev->pdsurfDiscardableList == NULL)
  327. ppdev->pdsurfDiscardableList = pdsurf;
  328. else
  329. {
  330. for (pTmp = ppdev->pdsurfDiscardableList;
  331. pTmp->pdsurfDiscardableNext != NULL;
  332. pTmp = pTmp->pdsurfDiscardableNext)
  333. ;
  334. pTmp->pdsurfDiscardableNext = pdsurf;
  335. }
  336. return(hbmDevice);
  337. }
  338. EngDeleteSurface((HSURF) hbmDevice);
  339. }
  340. vVidMemFree(pdsurf);
  341. return(0);
  342. }
  343. } while (bMoveOldestOffscreenDfbToDib(ppdev));
  344. return(0);
  345. }
  346. /******************************Public*Routine******************************\
  347. * HBITMAP DrvDeriveSurface
  348. *
  349. * This function is new to NT5, and allows the driver to accelerate any
  350. * GDI drawing to a DirectDraw surface.
  351. *
  352. * Note the similarity of this function to DrvCreateDeviceBitmap.
  353. *
  354. \**************************************************************************/
  355. HBITMAP DrvDeriveSurface(
  356. DD_DIRECTDRAW_GLOBAL* lpDirectDraw,
  357. DD_SURFACE_LOCAL* lpLocal)
  358. {
  359. PDEV* ppdev;
  360. DSURF* pdsurf;
  361. HBITMAP hbmDevice;
  362. DD_SURFACE_GLOBAL* lpSurface;
  363. SIZEL sizl;
  364. ppdev = (PDEV*) lpDirectDraw->dhpdev;
  365. lpSurface = lpLocal->lpGbl;
  366. // GDI should never call us for a non-RGB surface, but let's assert just
  367. // to make sure they're doing their job properly.
  368. ASSERTDD(!(lpSurface->ddpfSurface.dwFlags & DDPF_FOURCC),
  369. "GDI called us with a non-RGB surface!");
  370. // The rest of our driver expects GDI calls to come in with the same
  371. // format as the primary surface. So we'd better not wrap a device
  372. // bitmap around an RGB format that the rest of our driver doesn't
  373. // understand. Also, we must check to see that it is not a surface
  374. // whose pitch does not match the primary surface.
  375. // NOTE: Most surfaces created by this driver are allocated as 2D surfaces
  376. // whose lPitch's are equal to the screen pitch. However, overlay surfaces
  377. // are allocated such that there lPitch's are usually different then the
  378. // screen pitch. The hardware can not accelerate drawing operations to
  379. // these surfaces and thus we fail to derive these surfaces.
  380. if (lpSurface->ddpfSurface.dwRGBBitCount == (DWORD) ppdev->cjPelSize * 8 &&
  381. lpSurface->lPitch == ppdev->lDelta)
  382. {
  383. pdsurf = EngAllocMem(FL_ZERO_MEMORY, sizeof(DSURF), ALLOC_TAG);
  384. if (pdsurf != NULL)
  385. {
  386. sizl.cx = lpSurface->wWidth;
  387. sizl.cy = lpSurface->wHeight;
  388. hbmDevice = EngCreateDeviceBitmap((DHSURF) pdsurf,
  389. sizl,
  390. ppdev->iBitmapFormat);
  391. if (hbmDevice != NULL)
  392. {
  393. // Note that HOOK_SYNCHRONIZE must always be hooked when
  394. // we give GDI a pointer to the bitmap bits.
  395. if (EngModifySurface((HSURF) hbmDevice,
  396. ppdev->hdevEng,
  397. ppdev->flHooks | HOOK_SYNCHRONIZE,
  398. MS_NOTSYSTEMMEMORY, // It's video-memory
  399. (DHSURF) pdsurf,
  400. ppdev->pjScreen + lpSurface->fpVidMem,
  401. lpSurface->lPitch,
  402. NULL))
  403. {
  404. pdsurf->dt = DT_DIRECTDRAW;
  405. pdsurf->ppdev = ppdev;
  406. pdsurf->x = lpSurface->xHint;
  407. pdsurf->y = lpSurface->yHint;
  408. pdsurf->cx = lpSurface->wWidth;
  409. pdsurf->cy = lpSurface->wHeight;
  410. pdsurf->fpVidMem = lpSurface->fpVidMem;
  411. return(hbmDevice);
  412. }
  413. EngDeleteSurface((HSURF) hbmDevice);
  414. }
  415. EngFreeMem(pdsurf);
  416. }
  417. }
  418. return(0);
  419. }
  420. /******************************Public*Routine******************************\
  421. * VOID DrvDeleteDeviceBitmap
  422. *
  423. * Deletes a DFB.
  424. *
  425. \**************************************************************************/
  426. VOID DrvDeleteDeviceBitmap(
  427. DHSURF dhsurf)
  428. {
  429. DSURF* pdsurf;
  430. PDEV* ppdev;
  431. DSURF* pTmp;
  432. pdsurf = (DSURF*) dhsurf;
  433. ppdev = pdsurf->ppdev;
  434. if ((pdsurf->dt & (DT_DIB | DT_DIRECTDRAW)) == 0)
  435. {
  436. // It's a surface stashed in video memory, so we have to remove
  437. // it from the discardable surface list:
  438. if (ppdev->pdsurfDiscardableList == pdsurf)
  439. ppdev->pdsurfDiscardableList = pdsurf->pdsurfDiscardableNext;
  440. else
  441. {
  442. for (pTmp = ppdev->pdsurfDiscardableList;
  443. pTmp->pdsurfDiscardableNext != pdsurf;
  444. pTmp = pTmp->pdsurfDiscardableNext)
  445. ;
  446. pTmp->pdsurfDiscardableNext = pdsurf->pdsurfDiscardableNext;
  447. }
  448. }
  449. vVidMemFree(pdsurf);
  450. }
  451. /******************************Public*Routine******************************\
  452. * BOOL bAssertModeOffscreenHeap
  453. *
  454. * This function is called whenever we switch in or out of full-screen
  455. * mode. We have to convert all the off-screen bitmaps to DIBs when
  456. * we switch to full-screen (because we may be asked to draw on them even
  457. * when in full-screen, and the mode switch would probably nuke the video
  458. * memory contents anyway).
  459. *
  460. \**************************************************************************/
  461. BOOL bAssertModeOffscreenHeap(
  462. PDEV* ppdev,
  463. BOOL bEnable)
  464. {
  465. BOOL b;
  466. b = TRUE;
  467. if (!bEnable)
  468. {
  469. b = bMoveAllDfbsFromOffscreenToDibs(ppdev);
  470. }
  471. return(b);
  472. }
  473. /******************************Public*Routine******************************\
  474. * VOID vDisableOffscreenHeap
  475. *
  476. * Frees any resources allocated by the off-screen heap.
  477. *
  478. \**************************************************************************/
  479. VOID vDisableOffscreenHeap(
  480. PDEV* ppdev)
  481. {
  482. SURFOBJ* psoPunt;
  483. HSURF hsurf;
  484. psoPunt = ppdev->psoPunt;
  485. if (psoPunt != NULL)
  486. {
  487. hsurf = psoPunt->hsurf;
  488. EngUnlockSurface(psoPunt);
  489. EngDeleteSurface(hsurf);
  490. }
  491. psoPunt = ppdev->psoPunt2;
  492. if (psoPunt != NULL)
  493. {
  494. hsurf = psoPunt->hsurf;
  495. EngUnlockSurface(psoPunt);
  496. EngDeleteSurface(hsurf);
  497. }
  498. }
  499. /******************************Public*Routine******************************\
  500. * BOOL bEnableOffscreenHeap
  501. *
  502. * Initializes the off-screen heap using all available video memory,
  503. * accounting for the portion taken by the visible screen.
  504. *
  505. \**************************************************************************/
  506. BOOL bEnableOffscreenHeap(
  507. PDEV* ppdev)
  508. {
  509. SIZEL sizl;
  510. HSURF hsurf;
  511. // Allocate a 'punt' SURFOBJ we'll use when the device-bitmap is in
  512. // off-screen memory, but we want GDI to draw to it directly as an
  513. // engine-managed surface:
  514. sizl.cx = ppdev->cxMemory;
  515. sizl.cy = ppdev->cyMemory;
  516. // We want to create it with exactly the same hooks and capabilities
  517. // as our primary surface. We will override the 'lDelta' and 'pvScan0'
  518. // fields later:
  519. hsurf = (HSURF) EngCreateBitmap(sizl,
  520. 0xbadf00d,
  521. ppdev->iBitmapFormat,
  522. BMF_TOPDOWN,
  523. (VOID*) 0xbadf00d);
  524. // We don't want GDI to turn around and call any of our Drv drawing
  525. // functions when drawing to these surfaces, so always set the hooks
  526. // to '0':
  527. if ((hsurf == 0) ||
  528. (!EngAssociateSurface(hsurf, ppdev->hdevEng, 0)) ||
  529. (!(ppdev->psoPunt = EngLockSurface(hsurf))))
  530. {
  531. DISPDBG((1, "Failed punt surface creation"));
  532. EngDeleteSurface(hsurf);
  533. goto ReturnFalse;
  534. }
  535. // We don't want GDI to turn around and call any of our Drv drawing
  536. // functions when drawing to these surfaces, so always set the hooks
  537. // to '0':
  538. hsurf = (HSURF) EngCreateBitmap(sizl,
  539. 0xbadf00d,
  540. ppdev->iBitmapFormat,
  541. BMF_TOPDOWN,
  542. (VOID*) 0xbadf00d);
  543. // We don't want GDI to call us back when drawing to these surfaces,
  544. // so always set the hooks to '0':
  545. if ((hsurf == 0) ||
  546. (!EngAssociateSurface(hsurf, ppdev->hdevEng, 0)) ||
  547. (!(ppdev->psoPunt2 = EngLockSurface(hsurf))))
  548. {
  549. DISPDBG((1, "Failed punt surface creation"));
  550. EngDeleteSurface(hsurf);
  551. goto ReturnFalse;
  552. }
  553. DISPDBG((5, "Passed bEnableOffscreenHeap"));
  554. return(TRUE);
  555. ReturnFalse:
  556. DISPDBG((1, "Failed bEnableOffscreenHeap"));
  557. return(FALSE);
  558. }