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.

662 lines
20 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: ddraw.c
  3. *
  4. * Implements all the DirectDraw components for the driver.
  5. *
  6. * Copyright (c) 1995-1996 Microsoft Corporation
  7. \**************************************************************************/
  8. #include "precomp.h"
  9. #define VBLANK_IS_ACTIVE(pjBase) \
  10. (READ_PORT_UCHAR(pjBase + VGA_BASE + IN_STAT_1) & 0x8)
  11. #define DISPLAY_IS_ACTIVE(pjBase) \
  12. (!(READ_PORT_UCHAR(pjBase + VGA_BASE + IN_STAT_1) & 0x1))
  13. #define START_ADDRESS_HIGH 0x0C // Index for Frame Buffer Start
  14. /******************************Public*Routine******************************\
  15. * VOID vGetDisplayDuration
  16. *
  17. * Get the length, in EngQueryPerformanceCounter() ticks, of a refresh cycle.
  18. *
  19. * If we could trust the miniport to return back and accurate value for
  20. * the refresh rate, we could use that. Unfortunately, our miniport doesn't
  21. * ensure that it's an accurate value.
  22. *
  23. \**************************************************************************/
  24. #define NUM_VBLANKS_TO_MEASURE 1
  25. #define NUM_MEASUREMENTS_TO_TAKE 8
  26. VOID vGetDisplayDuration(
  27. PDEV* ppdev)
  28. {
  29. BYTE* pjBase;
  30. LONG i;
  31. LONG j;
  32. LONGLONG li;
  33. LONGLONG liFrequency;
  34. LONGLONG liMin;
  35. LONGLONG aliMeasurement[NUM_MEASUREMENTS_TO_TAKE + 1];
  36. pjBase = ppdev->pjBase;
  37. memset(&ppdev->flipRecord, 0, sizeof(ppdev->flipRecord));
  38. // Warm up EngQUeryPerformanceCounter to make sure it's in the working
  39. // set:
  40. EngQueryPerformanceCounter(&li);
  41. // Unfortunately, since NT is a proper multitasking system, we can't
  42. // just disable interrupts to take an accurate reading. We also can't
  43. // do anything so goofy as dynamically change our thread's priority to
  44. // real-time.
  45. //
  46. // So we just do a bunch of short measurements and take the minimum.
  47. //
  48. // It would be 'okay' if we got a result that's longer than the actual
  49. // VBlank cycle time -- nothing bad would happen except that the app
  50. // would run a little slower. We don't want to get a result that's
  51. // shorter than the actual VBlank cycle time -- that could cause us
  52. // to start drawing over a frame before the Flip has occured.
  53. //
  54. // Skip a couple of vertical blanks to allow the hardware to settle
  55. // down after the mode change, to make our readings accurate:
  56. for (i = 2; i != 0; i--)
  57. {
  58. while (VBLANK_IS_ACTIVE(pjBase))
  59. ;
  60. while (!(VBLANK_IS_ACTIVE(pjBase)))
  61. ;
  62. }
  63. for (i = 0; i < NUM_MEASUREMENTS_TO_TAKE; i++)
  64. {
  65. // We're at the start of the VBlank active cycle!
  66. EngQueryPerformanceCounter(&aliMeasurement[i]);
  67. // Okay, so life in a multi-tasking environment isn't all that
  68. // simple. What if we had taken a context switch just before
  69. // the above EngQueryPerformanceCounter call, and now were half
  70. // way through the VBlank inactive cycle? Then we would measure
  71. // only half a VBlank cycle, which is obviously bad. The worst
  72. // thing we can do is get a time shorter than the actual VBlank
  73. // cycle time.
  74. //
  75. // So we solve this by making sure we're in the VBlank active
  76. // time before and after we query the time. If it's not, we'll
  77. // sync up to the next VBlank (it's okay to measure this period --
  78. // it will be guaranteed to be longer than the VBlank cycle and
  79. // will likely be thrown out when we select the minimum sample).
  80. // There's a chance that we'll take a context switch and return
  81. // just before the end of the active VBlank time -- meaning that
  82. // the actual measured time would be less than the true amount --
  83. // but since the VBlank is active less than 1% of the time, this
  84. // means that we would have a maximum of 1% error approximately
  85. // 1% of the times we take a context switch. An acceptable risk.
  86. //
  87. // This next line will cause us wait if we're no longer in the
  88. // VBlank active cycle as we should be at this point:
  89. while (!(VBLANK_IS_ACTIVE(pjBase)))
  90. ;
  91. for (j = 0; j < NUM_VBLANKS_TO_MEASURE; j++)
  92. {
  93. while (VBLANK_IS_ACTIVE(pjBase))
  94. ;
  95. while (!(VBLANK_IS_ACTIVE(pjBase)))
  96. ;
  97. }
  98. }
  99. EngQueryPerformanceCounter(&aliMeasurement[NUM_MEASUREMENTS_TO_TAKE]);
  100. // Use the minimum:
  101. liMin = aliMeasurement[1] - aliMeasurement[0];
  102. DISPDBG((1, "Refresh count: %li - %li", 1, (ULONG) liMin));
  103. for (i = 2; i <= NUM_MEASUREMENTS_TO_TAKE; i++)
  104. {
  105. li = aliMeasurement[i] - aliMeasurement[i - 1];
  106. DISPDBG((1, " %li - %li", i, (ULONG) li));
  107. if (li < liMin)
  108. liMin = li;
  109. }
  110. // Round the result:
  111. ppdev->flipRecord.liFlipDuration
  112. = (DWORD) (liMin + (NUM_VBLANKS_TO_MEASURE / 2)) / NUM_VBLANKS_TO_MEASURE;
  113. ppdev->flipRecord.bFlipFlag = FALSE;
  114. ppdev->flipRecord.fpFlipFrom = 0;
  115. // We need the refresh rate in Hz to query the S3 miniport about the
  116. // streams parameters:
  117. EngQueryPerformanceFrequency(&liFrequency);
  118. DISPDBG((1, "Frequency %li.%03li Hz",
  119. (ULONG) (EngQueryPerformanceFrequency(&li),
  120. li / ppdev->flipRecord.liFlipDuration),
  121. (ULONG) (EngQueryPerformanceFrequency(&li),
  122. ((li * 1000) / ppdev->flipRecord.liFlipDuration) % 1000)));
  123. }
  124. /******************************Public*Routine******************************\
  125. * HRESULT ddrvalUpdateFlipStatus
  126. *
  127. * Checks and sees if the most recent flip has occurred.
  128. *
  129. * Unfortunately, the hardware has no ability to tell us whether a vertical
  130. * retrace has occured since the flip command was given other than by
  131. * sampling the vertical-blank-active and display-active status bits.
  132. *
  133. \**************************************************************************/
  134. HRESULT ddrvalUpdateFlipStatus(
  135. PDEV* ppdev,
  136. FLATPTR fpVidMem)
  137. {
  138. BYTE* pjBase;
  139. LONGLONG liTime;
  140. pjBase = ppdev->pjBase;
  141. if ((ppdev->flipRecord.bFlipFlag) &&
  142. ((fpVidMem == (FLATPTR) -1) ||
  143. (fpVidMem == ppdev->flipRecord.fpFlipFrom)))
  144. {
  145. if (VBLANK_IS_ACTIVE(pjBase))
  146. {
  147. if (ppdev->flipRecord.bWasEverInDisplay)
  148. {
  149. ppdev->flipRecord.bHaveEverCrossedVBlank = TRUE;
  150. }
  151. }
  152. else if (DISPLAY_IS_ACTIVE(pjBase))
  153. {
  154. if (ppdev->flipRecord.bHaveEverCrossedVBlank)
  155. {
  156. ppdev->flipRecord.bFlipFlag = FALSE;
  157. return(DD_OK);
  158. }
  159. ppdev->flipRecord.bWasEverInDisplay = TRUE;
  160. }
  161. // It's pretty unlikely that we'll happen to sample the vertical-
  162. // blank-active at the first vertical blank after the flip command
  163. // has been given. So to provide better results, we also check the
  164. // time elapsed since the flip. If it's more than the duration of
  165. // one entire refresh of the display, then we know for sure it has
  166. // happened:
  167. EngQueryPerformanceCounter(&liTime);
  168. if (liTime - ppdev->flipRecord.liFlipTime
  169. <= ppdev->flipRecord.liFlipDuration)
  170. {
  171. return(DDERR_WASSTILLDRAWING);
  172. }
  173. ppdev->flipRecord.bFlipFlag = FALSE;
  174. }
  175. return(DD_OK);
  176. }
  177. /******************************Public*Routine******************************\
  178. * DWORD DdMapMemory
  179. *
  180. * This is a new DDI call specific to Windows NT that is used to map
  181. * or unmap all the application modifiable portions of the frame buffer
  182. * into the specified process's address space.
  183. *
  184. \**************************************************************************/
  185. DWORD DdMapMemory(
  186. PDD_MAPMEMORYDATA lpMapMemory)
  187. {
  188. PDEV* ppdev;
  189. ppdev = (PDEV*) lpMapMemory->lpDD->dhpdev;
  190. // By returning DDHAL_DRIVER_NOTHANDLED and setting 'bMap' to -1, we
  191. // have GDI take care of mapping the section that is our 'shadow buffer'
  192. // directly into the application's address space. We tell GDI our kernel
  193. // mode address by sticking it in 'fpProcess':
  194. lpMapMemory->fpProcess = (FLATPTR) ppdev->pjScreen;
  195. lpMapMemory->bMap = (BOOL) -1;
  196. return(DDHAL_DRIVER_NOTHANDLED);
  197. }
  198. /******************************Public*Routine******************************\
  199. * DWORD DdWaitForVerticalBlank
  200. *
  201. * 3-Dec-1995 -by- J. Andrew Goossen [andrewgo]
  202. * Wrote it.
  203. \**************************************************************************/
  204. DWORD DdWaitForVerticalBlank(
  205. PDD_WAITFORVERTICALBLANKDATA lpWaitForVerticalBlank)
  206. {
  207. PDEV* ppdev;
  208. BYTE* pjBase;
  209. ppdev = (PDEV*) lpWaitForVerticalBlank->lpDD->dhpdev;
  210. pjBase = ppdev->pjBase;
  211. lpWaitForVerticalBlank->ddRVal = DD_OK;
  212. switch (lpWaitForVerticalBlank->dwFlags)
  213. {
  214. case DDWAITVB_I_TESTVB:
  215. lpWaitForVerticalBlank->bIsInVB = (VBLANK_IS_ACTIVE(pjBase) != 0);
  216. break;
  217. case DDWAITVB_BLOCKBEGIN:
  218. while (VBLANK_IS_ACTIVE(pjBase))
  219. ;
  220. while (!VBLANK_IS_ACTIVE(pjBase))
  221. ;
  222. break;
  223. case DDWAITVB_BLOCKEND:
  224. while (!VBLANK_IS_ACTIVE(pjBase))
  225. ;
  226. while (VBLANK_IS_ACTIVE(pjBase))
  227. ;
  228. break;
  229. }
  230. return(DDHAL_DRIVER_HANDLED);
  231. }
  232. /******************************Public*Routine******************************\
  233. * DWORD DdLock
  234. *
  235. \**************************************************************************/
  236. DWORD DdLock(
  237. PDD_LOCKDATA lpLock)
  238. {
  239. PDEV* ppdev;
  240. DD_SURFACE_LOCAL* lpSurfaceLocal;
  241. ppdev = (PDEV*) lpLock->lpDD->dhpdev;
  242. lpSurfaceLocal = lpLock->lpDDSurface;
  243. if (lpSurfaceLocal->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
  244. {
  245. // If the application is locking the currently visible flip
  246. // surface, remember the bounds of its lock so that we can
  247. // use it at Unlock time to update the physical display:
  248. ppdev->cLocks++;
  249. if ((ppdev->cLocks == 1) && (lpLock->bHasRect))
  250. {
  251. ppdev->rclLock = lpLock->rArea;
  252. }
  253. else
  254. {
  255. // If we were real keen, we would union the new area with
  256. // the old. But we're not:
  257. ppdev->rclLock.top = 0;
  258. ppdev->rclLock.left = 0;
  259. ppdev->rclLock.right = ppdev->cxScreen;
  260. ppdev->rclLock.bottom = ppdev->cyScreen;
  261. }
  262. }
  263. return(DDHAL_DRIVER_NOTHANDLED);
  264. }
  265. /******************************Public*Routine******************************\
  266. * DWORD DdUnlock
  267. *
  268. * 3-Dec-1995 -by- J. Andrew Goossen [andrewgo]
  269. * Wrote it.
  270. \**************************************************************************/
  271. DWORD DdUnlock(
  272. PDD_UNLOCKDATA lpUnlock)
  273. {
  274. PDEV* ppdev;
  275. DD_SURFACE_LOCAL* lpSurfaceLocal;
  276. ppdev = (PDEV*) lpUnlock->lpDD->dhpdev;
  277. lpSurfaceLocal = lpUnlock->lpDDSurface;
  278. // If this flip buffer is visible, then we have to update the physical
  279. // screen with the shadow contents.
  280. if (lpSurfaceLocal->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
  281. {
  282. vUpdate(ppdev, &ppdev->rclLock, NULL);
  283. ppdev->cLocks--;
  284. ASSERTDD(ppdev->cLocks >= 0, "Invalid lock count");
  285. }
  286. return(DDHAL_DRIVER_NOTHANDLED);
  287. }
  288. /******************************Public*Routine******************************\
  289. * DWORD DdFlip
  290. *
  291. * 3-Dec-1995 -by- J. Andrew Goossen [andrewgo]
  292. * Wrote it.
  293. \**************************************************************************/
  294. DWORD DdFlip(
  295. PDD_FLIPDATA lpFlip)
  296. {
  297. PDEV* ppdev;
  298. BYTE* pjBase;
  299. HRESULT ddrval;
  300. ULONG cDwordsPerPlane;
  301. BYTE* pjSourceStart;
  302. BYTE* pjDestinationStart;
  303. BYTE* pjSource;
  304. BYTE* pjDestination;
  305. LONG iPage;
  306. LONG i;
  307. ULONG ul;
  308. FLATPTR fpVidMem;
  309. ppdev = (PDEV*) lpFlip->lpDD->dhpdev;
  310. pjBase = ppdev->pjBase;
  311. // Is the current flip still in progress?
  312. //
  313. // Don't want a flip to work until after the last flip is done,
  314. // so we ask for the general flip status and ignore the vmem.
  315. ddrval = ddrvalUpdateFlipStatus(ppdev, (FLATPTR) -1);
  316. if (ddrval != DD_OK)
  317. {
  318. lpFlip->ddRVal = DDERR_WASSTILLDRAWING;
  319. return(DDHAL_DRIVER_HANDLED);
  320. }
  321. // Make the following page the current back-buffer. We always flip
  322. // between three pages, so watch for our limit:
  323. ppdev->cjVgaOffset += ppdev->cjVgaPageSize;
  324. if (++ppdev->iVgaPage == ppdev->cVgaPages)
  325. {
  326. ppdev->iVgaPage = 0;
  327. ppdev->cjVgaOffset = 0;
  328. }
  329. // Copy from the DIB surface to the current VGA back-buffer. We have
  330. // to convert to planar format on the way:
  331. pjDestinationStart = ppdev->pjVga + ppdev->cjVgaOffset;
  332. fpVidMem = lpFlip->lpSurfTarg->lpGbl->fpVidMem;
  333. pjSourceStart = ppdev->pjScreen + fpVidMem;
  334. cDwordsPerPlane = ppdev->cDwordsPerPlane;
  335. // Remember what DirectDraw surface is currently 'visible':
  336. ppdev->fpScreenOffset = fpVidMem;
  337. // Now do the blt!
  338. WRITE_PORT_UCHAR(pjBase + VGA_BASE + SEQ_ADDR, SEQ_MAP_MASK);
  339. for (iPage = 0; iPage < 4; iPage++, pjSourceStart++)
  340. {
  341. WRITE_PORT_UCHAR(pjBase + VGA_BASE + SEQ_DATA, 1 << iPage);
  342. #if defined(_X86_)
  343. _asm {
  344. mov esi,pjSourceStart
  345. mov edi,pjDestinationStart
  346. mov ecx,cDwordsPerPlane
  347. PixelLoop:
  348. mov al,[esi+8]
  349. mov ah,[esi+12]
  350. shl eax,16
  351. mov al,[esi]
  352. mov ah,[esi+4]
  353. mov [edi],eax
  354. add edi,4
  355. add esi,16
  356. dec ecx
  357. jnz PixelLoop
  358. }
  359. #else
  360. pjSource = pjSourceStart;
  361. pjDestination = pjDestinationStart;
  362. for (i = cDwordsPerPlane; i != 0; i--)
  363. {
  364. ul = (*(pjSource))
  365. | (*(pjSource + 4) << 8)
  366. | (*(pjSource + 8) << 16)
  367. | (*(pjSource + 12) << 24);
  368. WRITE_REGISTER_ULONG((ULONG*) pjDestination, ul);
  369. pjDestination += 4;
  370. pjSource += 16;
  371. }
  372. #endif
  373. }
  374. // Now flip to the page we just updated:
  375. WRITE_PORT_USHORT((USHORT*) (pjBase + VGA_BASE + CRTC_ADDR),
  376. (USHORT) ((ppdev->cjVgaOffset) & 0xff00) | START_ADDRESS_HIGH);
  377. // Remember where and when we were when we did the flip:
  378. EngQueryPerformanceCounter(&ppdev->flipRecord.liFlipTime);
  379. ppdev->flipRecord.bFlipFlag = TRUE;
  380. ppdev->flipRecord.bHaveEverCrossedVBlank = FALSE;
  381. ppdev->flipRecord.bWasEverInDisplay = FALSE;
  382. ppdev->flipRecord.fpFlipFrom = lpFlip->lpSurfCurr->lpGbl->fpVidMem;
  383. lpFlip->ddRVal = DD_OK;
  384. return(DDHAL_DRIVER_HANDLED);
  385. }
  386. /******************************Public*Routine******************************\
  387. * BOOL DrvGetDirectDrawInfo
  388. *
  389. * 3-Dec-1995 -by- J. Andrew Goossen [andrewgo]
  390. * Wrote it.
  391. \**************************************************************************/
  392. BOOL DrvGetDirectDrawInfo(
  393. DHPDEV dhpdev,
  394. DD_HALINFO* pHalInfo,
  395. DWORD* pdwNumHeaps,
  396. VIDEOMEMORY* pvmList, // Will be NULL on first call
  397. DWORD* pdwNumFourCC,
  398. DWORD* pdwFourCC) // Will be NULL on first call
  399. {
  400. PDEV* ppdev;
  401. ppdev = (PDEV*) dhpdev;
  402. pHalInfo->dwSize = sizeof(*pHalInfo);
  403. // Current primary surface attributes. Since HalInfo is zero-initialized
  404. // by GDI, we only have to fill in the fields which should be non-zero:
  405. pHalInfo->vmiData.dwDisplayWidth = ppdev->cxScreen;
  406. pHalInfo->vmiData.dwDisplayHeight = ppdev->cyScreen;
  407. pHalInfo->vmiData.lDisplayPitch = ppdev->lScreenDelta;
  408. pHalInfo->vmiData.pvPrimary = ppdev->pjScreen;
  409. pHalInfo->vmiData.ddpfDisplay.dwSize = sizeof(DDPIXELFORMAT);
  410. pHalInfo->vmiData.ddpfDisplay.dwFlags = DDPF_RGB | DDPF_PALETTEINDEXED8;
  411. pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount = 8;
  412. // These masks will be zero at 8bpp:
  413. pHalInfo->vmiData.ddpfDisplay.dwRBitMask = 0;
  414. pHalInfo->vmiData.ddpfDisplay.dwGBitMask = 0;
  415. pHalInfo->vmiData.ddpfDisplay.dwBBitMask = 0;
  416. pHalInfo->vmiData.ddpfDisplay.dwRGBAlphaBitMask = 0;
  417. *pdwNumHeaps = 0;
  418. if (ppdev->cyMemory != ppdev->cyScreen)
  419. {
  420. *pdwNumHeaps = 1;
  421. if (pvmList != NULL)
  422. {
  423. pvmList->dwFlags = VIDMEM_ISRECTANGULAR;
  424. pvmList->fpStart = ppdev->cyScreen * ppdev->lScreenDelta;
  425. pvmList->dwWidth = ppdev->lScreenDelta;
  426. pvmList->dwHeight = ppdev->cyMemory - ppdev->cyScreen;
  427. pvmList->ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
  428. }
  429. }
  430. // Capabilities supported:
  431. pHalInfo->ddCaps.dwFXCaps = 0;
  432. pHalInfo->ddCaps.dwCaps = 0;
  433. pHalInfo->ddCaps.dwCKeyCaps = 0;
  434. pHalInfo->ddCaps.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN
  435. | DDSCAPS_PRIMARYSURFACE
  436. | DDSCAPS_FLIP;
  437. // Required alignments of the scan lines for each kind of memory:
  438. pHalInfo->vmiData.dwOffscreenAlign = 4;
  439. // FourCCs supported:
  440. *pdwNumFourCC = 0;
  441. return(TRUE);
  442. }
  443. /******************************Public*Routine******************************\
  444. * BOOL DrvEnableDirectDraw
  445. *
  446. * 3-Dec-1995 -by- J. Andrew Goossen [andrewgo]
  447. * Wrote it.
  448. \**************************************************************************/
  449. BOOL DrvEnableDirectDraw(
  450. DHPDEV dhpdev,
  451. DD_CALLBACKS* pCallBacks,
  452. DD_SURFACECALLBACKS* pSurfaceCallBacks,
  453. DD_PALETTECALLBACKS* pPaletteCallBacks)
  454. {
  455. pCallBacks->WaitForVerticalBlank = DdWaitForVerticalBlank;
  456. pCallBacks->MapMemory = DdMapMemory;
  457. pCallBacks->dwFlags = DDHAL_CB32_WAITFORVERTICALBLANK
  458. | DDHAL_CB32_MAPMEMORY;
  459. pSurfaceCallBacks->Flip = DdFlip;
  460. pSurfaceCallBacks->Lock = DdLock;
  461. pSurfaceCallBacks->Unlock = DdUnlock;
  462. pSurfaceCallBacks->dwFlags = DDHAL_SURFCB32_FLIP
  463. | DDHAL_SURFCB32_LOCK
  464. | DDHAL_SURFCB32_UNLOCK;
  465. return(TRUE);
  466. }
  467. /******************************Public*Routine******************************\
  468. * VOID DrvDisableDirectDraw
  469. *
  470. * 3-Dec-1995 -by- J. Andrew Goossen [andrewgo]
  471. * Wrote it.
  472. \**************************************************************************/
  473. VOID DrvDisableDirectDraw(
  474. DHPDEV dhpdev)
  475. {
  476. }
  477. /******************************Public*Routine******************************\
  478. * BOOL bEnableDirectDraw
  479. *
  480. * This function is called by enable.c when the mode is first initialized,
  481. * right after the miniport does the mode-set.
  482. *
  483. \**************************************************************************/
  484. BOOL bEnableDirectDraw(
  485. PDEV* ppdev)
  486. {
  487. // Calculate the total number of dwords per plane for flipping:
  488. ppdev->cDwordsPerPlane = (ppdev->cyScreen * ppdev->lVgaDelta) >> 2;
  489. // We only program the high byte of the VGA offset, so the page size must
  490. // be a multiple of 256:
  491. ppdev->cjVgaPageSize = ((ppdev->cyScreen * ppdev->lVgaDelta) + 255) & ~255;
  492. // VGAs can address only 64k of memory, so that limits the number of
  493. // page-flip buffers we can have:
  494. ppdev->cVgaPages = 64 * 1024 / ppdev->cjVgaPageSize;
  495. // Accurately measure the refresh rate for later:
  496. vGetDisplayDuration(ppdev);
  497. return(TRUE);
  498. }
  499. /******************************Public*Routine******************************\
  500. * VOID vAssertModeDirectDraw
  501. *
  502. * This function is called by enable.c when entering or leaving the
  503. * DOS full-screen character mode.
  504. *
  505. \**************************************************************************/
  506. VOID vAssertModeDirectDraw(
  507. PDEV* ppdev,
  508. BOOL bEnable)
  509. {
  510. }
  511. /******************************Public*Routine******************************\
  512. * VOID vDisableDirectDraw
  513. *
  514. * This function is called by enable.c when the driver is shutting down.
  515. *
  516. \**************************************************************************/
  517. VOID vDisableDirectDraw(
  518. PDEV* ppdev)
  519. {
  520. ASSERTDD(ppdev->cLocks == 0, "Invalid lock count");
  521. }