|
|
/******************************Module*Header*******************************\
* * ******************* * * GDI SAMPLE CODE * * ******************* * * Module Name: heap.c * * This module contains the routines for an off-screen video heap manager. * It is used primarily for allocating space for device-format-bitmaps in * off-screen memory. * * Off-screen bitmaps are a big deal on NT because: * * 1) It reduces the working set. Any bitmap stored in off-screen * memory is a bitmap that isn't taking up space in main memory. * * 2) There is a speed win by using the accelerator hardware for * drawing, in place of NT's GDI code. NT's GDI is written entirely * in 'C++' and perhaps isn't as fast as it could be. * * 3) It raises your Winbench score. * * Copyright (c) 1993-1998 Microsoft Corporation \**************************************************************************/
#include "precomp.h"
/******************************Public*Routine******************************\
* DSURF* pVidMemAllocate * \**************************************************************************/
DSURF* pVidMemAllocate( PDEV* ppdev, LONG cx, LONG cy) { ULONG iHeap; VIDEOMEMORY* pvmHeap; FLATPTR fpVidMem; DSURF* pdsurf; LONG lDelta; SURFACEALIGNMENT Alignment;
memset(&Alignment, 0, sizeof(Alignment));
// Ensure quadword x-alignment in video memory:
Alignment.Rectangular.dwXAlignment = 8; Alignment.Rectangular.dwFlags |= SURFACEALIGN_DISCARDABLE;
for (iHeap = 0; iHeap < ppdev->cHeaps; iHeap++) { pvmHeap = &ppdev->pvmList[iHeap];
// AGP memory could be potentially used for device-bitmaps, with
// two very large caveats:
//
// 1. No kernel-mode view is made for the AGP memory (would take
// up too many PTEs and too much virtual address space).
// No user-mode view is made either unless a DirectDraw
// application happens to be running. Consequently, neither
// GDI nor the driver can use the CPU to directly access the
// bits. (It can be done through the accelerator, however.)
//
// 2. AGP heaps never shrink their committed allocations. The
// only time AGP memory gets de-committed is when the entire
// heap is empty. And don't forget that committed AGP memory
// is non-pageable. Consequently, if you were to enable a
// 50 MB AGP heap for DirectDraw, and were sharing that heap
// for device bitmap allocations, after running a D3D game
// the system would never be able to free that 50 MB of non-
// pageable memory until every single device bitmap was deleted!
// Just watch your Winstone scores plummet if someone plays
// a D3D game first.
if (!(pvmHeap->dwFlags & VIDMEM_ISNONLOCAL)) { fpVidMem = HeapVidMemAllocAligned(pvmHeap, cx * ppdev->cjPelSize, cy, &Alignment, &lDelta); if (fpVidMem != 0) { pdsurf = EngAllocMem(FL_ZERO_MEMORY, sizeof(DSURF), ALLOC_TAG); if (pdsurf != NULL) { pdsurf->dt = 0; pdsurf->ppdev = ppdev; pdsurf->x = (LONG)(fpVidMem % ppdev->lDelta) / ppdev->cjPelSize; pdsurf->y = (LONG)(fpVidMem / ppdev->lDelta); pdsurf->cx = cx; pdsurf->cy = cy; pdsurf->fpVidMem = fpVidMem; pdsurf->pvmHeap = pvmHeap;
return(pdsurf); }
VidMemFree(pvmHeap->lpHeap, fpVidMem); } } }
return(NULL); }
/******************************Public*Routine******************************\
* VOID vVidMemFree * \**************************************************************************/
VOID vVidMemFree( DSURF* pdsurf) { DSURF* pTmp;
if (pdsurf == NULL) return;
if (!(pdsurf->dt & DT_DIRECTDRAW)) { if (pdsurf->dt & DT_DIB) { EngFreeMem(pdsurf->pvScan0); } else { // Update the uniqueness to show that space has been freed, so
// that we may decide to see if some DIBs can be moved back into
// off-screen memory:
pdsurf->ppdev->iHeapUniq++;
VidMemFree(pdsurf->pvmHeap->lpHeap, pdsurf->fpVidMem); } }
EngFreeMem(pdsurf); }
/******************************Public*Routine******************************\
* BOOL bMoveOldestOffscreenDfbToDib * \**************************************************************************/
BOOL bMoveOldestOffscreenDfbToDib( PDEV* ppdev) { DSURF* pdsurf; LONG lDelta; VOID* pvScan0; RECTL rclDst; POINTL ptlSrc; SURFOBJ soTmp;
pdsurf = ppdev->pdsurfDiscardableList; if (pdsurf != NULL) { // Make the system-memory scans quadword aligned:
lDelta = (pdsurf->cx * ppdev->cjPelSize + 7) & ~7;
// Note that there's no point in zero-initializing this memory:
pvScan0 = EngAllocMem(0, lDelta * pdsurf->cy, ALLOC_TAG);
if (pvScan0 != NULL) { // The following 'EngModifySurface' call tells GDI to
// modify the surface to point to system-memory for
// the bits, and changes what Drv calls we want to
// hook for the surface.
//
// By specifying the surface address, GDI will convert the
// surface to an STYPE_BITMAP surface (if necessary) and
// point the bits to the memory we just allocated. The
// next time we see it in a DrvBitBlt call, the 'dhsurf'
// field will still point to our 'pdsurf' structure.
//
// Note that we hook only CopyBits and BitBlt when we
// convert the device-bitmap to a system-memory surface.
// This is so that we don't have to worry about getting
// DrvTextOut, DrvLineTo, etc. calls on bitmaps that
// we've converted to system-memory -- GDI will just
// automatically do the drawing for us.
//
// However, we are still interested in seeing DrvCopyBits
// and DrvBitBlt calls involving this surface, because
// in those calls we take the opportunity to see if it's
// worth putting the device-bitmap back into video memory
// (if some room has been freed up).
if (EngModifySurface(pdsurf->hsurf, ppdev->hdevEng, HOOK_COPYBITS | HOOK_BITBLT, 0, // It's system-memory
(DHSURF) pdsurf, pvScan0, lDelta, NULL)) { // First, copy the bits from off-screen memory to the DIB:
rclDst.left = 0; rclDst.top = 0; rclDst.right = pdsurf->cx; rclDst.bottom = pdsurf->cy;
ptlSrc.x = pdsurf->x; ptlSrc.y = pdsurf->y;
soTmp.lDelta = lDelta; soTmp.pvScan0 = pvScan0;
vGetBits(ppdev, &soTmp, &rclDst, &ptlSrc);
// Now free the off-screen memory:
VidMemFree(pdsurf->pvmHeap->lpHeap, pdsurf->fpVidMem);
// Remove this node from the discardable list:
ASSERTDD(ppdev->pdsurfDiscardableList == pdsurf, "Expected node to be head of the list");
ppdev->pdsurfDiscardableList = pdsurf->pdsurfDiscardableNext;
pdsurf->pdsurfDiscardableNext = NULL; pdsurf->dt = DT_DIB; pdsurf->pvScan0 = pvScan0;
return(TRUE); }
EngFreeMem(pvScan0); } }
return(FALSE); }
/******************************Public*Routine******************************\
* BOOL bMoveEverythingFromOffscreenToDibs * * This function is used when we're about to enter full-screen mode, which * would wipe all our off-screen bitmaps. GDI can ask us to draw on * device bitmaps even when we're in full-screen mode, and we do NOT have * the option of stalling the call until we switch out of full-screen. * We have no choice but to move all the off-screen DFBs to DIBs. * * Returns TRUE if all DSURFs have been successfully moved. * \**************************************************************************/
BOOL bMoveAllDfbsFromOffscreenToDibs( PDEV* ppdev) { do {} while (bMoveOldestOffscreenDfbToDib(ppdev));
return(ppdev->pdsurfDiscardableList == NULL); }
/******************************Public*Routine******************************\
* BOOL bMoveDibToOffscreenDfbIfRoom * \**************************************************************************/
BOOL bMoveDibToOffscreenDfbIfRoom( PDEV* ppdev, DSURF* psurf) { return(FALSE); }
/******************************Public*Routine******************************\
* HBITMAP DrvCreateDeviceBitmap * * Function called by GDI to create a device-format-bitmap (DFB). We will * always try to allocate the bitmap in off-screen; if we can't, we simply * fail the call and GDI will create and manage the bitmap itself. * * Note: We do not have to zero the bitmap bits. GDI will automatically * call us via DrvBitBlt to zero the bits (which is a security * consideration). * \**************************************************************************/
HBITMAP DrvCreateDeviceBitmap( DHPDEV dhpdev, SIZEL sizl, ULONG iFormat) { PDEV* ppdev; DSURF* pdsurf; HBITMAP hbmDevice; BYTE* pjSurface; LONG lDelta; FLONG flHooks; DSURF* pTmp;
ppdev = (PDEV*) dhpdev;
// If we're in full-screen mode, we hardly have any off-screen memory
// in which to allocate a DFB.
if (!ppdev->bEnabled) return(0);
// We only support device bitmaps that are the same colour depth
// as our display.
//
// Actually, those are the only kind GDI will ever call us with,
// but we may as well check. Note that this implies you'll never
// get a crack at 1bpp bitmaps.
// Note: we can't create a device bitmap when the color depth is 24
// BPP. Otherwise, we will have problem in vBankStart when we hack the
// pbnk->pso->pvScan0 = ppdev->pjScreen - cjOffset
// + yOffset * ppdev->lDelta
// + CONVERT_TO_BYTES(xOffset, ppdev);
// this pvScan0 is not guaranteed be DWORD aligned
if ( (iFormat != ppdev->iBitmapFormat) ||(iFormat == BMF_24BPP) ) return(0);
// We don't want anything 8x8 or smaller -- they're typically brush
// patterns which we don't particularly want to stash in off-screen
// memory.
//
// Note if you're tempted to extend this philosophy to surfaces
// larger than 8x8: in NT5, software cursors will use device-bitmaps
// when possible, which is a big win when they're in video-memory
// because we avoid the horrendous reads from video memory whenever
// the cursor has to be redrawn. But the problem is that these
// are small! (Typically 16x16 to 32x32.)
if ((sizl.cx <= 8) && (sizl.cy <= 8)) return(0);
do { pdsurf = pVidMemAllocate(ppdev, sizl.cx, sizl.cy); if (pdsurf != NULL) { hbmDevice = EngCreateDeviceBitmap((DHSURF) pdsurf, sizl, iFormat); if (hbmDevice != NULL) { // If we're running on a card that can map all of off-screen
// video-memory, give a pointer to the bits to GDI so that
// it can draw directly on the bits when it wants to.
//
// Note that this requires that we hook DrvSynchronize and
// set HOOK_SYNCHRONIZE.
if ((ppdev->flCaps & CAPS_NEW_MMIO) && !(ppdev->flCaps & CAPS_NO_DIRECT_ACCESS)) { pjSurface = pdsurf->fpVidMem + ppdev->pjScreen; lDelta = ppdev->lDelta; flHooks = ppdev->flHooks | HOOK_SYNCHRONIZE; } else { pjSurface = NULL; lDelta = 0; flHooks = ppdev->flHooks; }
if (EngModifySurface((HSURF) hbmDevice, ppdev->hdevEng, flHooks, MS_NOTSYSTEMMEMORY, // It's video-memory
(DHSURF) pdsurf, pjSurface, lDelta, NULL)) { pdsurf->hsurf = (HSURF) hbmDevice;
// Add this to the tail of the discardable surface list:
if (ppdev->pdsurfDiscardableList == NULL) ppdev->pdsurfDiscardableList = pdsurf; else { for (pTmp = ppdev->pdsurfDiscardableList; pTmp->pdsurfDiscardableNext != NULL; pTmp = pTmp->pdsurfDiscardableNext) ;
pTmp->pdsurfDiscardableNext = pdsurf; }
return(hbmDevice); }
EngDeleteSurface((HSURF) hbmDevice); }
vVidMemFree(pdsurf);
return(0); } } while (bMoveOldestOffscreenDfbToDib(ppdev));
return(0); }
/******************************Public*Routine******************************\
* HBITMAP DrvDeriveSurface * * This function is new to NT5, and allows the driver to accelerate any * GDI drawing to a DirectDraw surface. * * Note the similarity of this function to DrvCreateDeviceBitmap. * \**************************************************************************/
HBITMAP DrvDeriveSurface( DD_DIRECTDRAW_GLOBAL* lpDirectDraw, DD_SURFACE_LOCAL* lpLocal) { PDEV* ppdev; DSURF* pdsurf; HBITMAP hbmDevice; DD_SURFACE_GLOBAL* lpSurface; SIZEL sizl;
ppdev = (PDEV*) lpDirectDraw->dhpdev;
lpSurface = lpLocal->lpGbl;
// GDI should never call us for a non-RGB surface, but let's assert just
// to make sure they're doing their job properly.
ASSERTDD(!(lpSurface->ddpfSurface.dwFlags & DDPF_FOURCC), "GDI called us with a non-RGB surface!");
// The rest of our driver expects GDI calls to come in with the same
// format as the primary surface. So we'd better not wrap a device
// bitmap around an RGB format that the rest of our driver doesn't
// understand. Also, we must check to see that it is not a surface
// whose pitch does not match the primary surface.
// NOTE: Most surfaces created by this driver are allocated as 2D surfaces
// whose lPitch's are equal to the screen pitch. However, overlay surfaces
// are allocated such that there lPitch's are usually different then the
// screen pitch. The hardware can not accelerate drawing operations to
// these surfaces and thus we fail to derive these surfaces.
if (lpSurface->ddpfSurface.dwRGBBitCount == (DWORD) ppdev->cjPelSize * 8 && lpSurface->lPitch == ppdev->lDelta) { pdsurf = EngAllocMem(FL_ZERO_MEMORY, sizeof(DSURF), ALLOC_TAG); if (pdsurf != NULL) { sizl.cx = lpSurface->wWidth; sizl.cy = lpSurface->wHeight;
hbmDevice = EngCreateDeviceBitmap((DHSURF) pdsurf, sizl, ppdev->iBitmapFormat); if (hbmDevice != NULL) { // Note that HOOK_SYNCHRONIZE must always be hooked when
// we give GDI a pointer to the bitmap bits.
if (EngModifySurface((HSURF) hbmDevice, ppdev->hdevEng, ppdev->flHooks | HOOK_SYNCHRONIZE, MS_NOTSYSTEMMEMORY, // It's video-memory
(DHSURF) pdsurf, ppdev->pjScreen + lpSurface->fpVidMem, lpSurface->lPitch, NULL)) { pdsurf->dt = DT_DIRECTDRAW; pdsurf->ppdev = ppdev; pdsurf->x = lpSurface->xHint; pdsurf->y = lpSurface->yHint; pdsurf->cx = lpSurface->wWidth; pdsurf->cy = lpSurface->wHeight; pdsurf->fpVidMem = lpSurface->fpVidMem;
return(hbmDevice); }
EngDeleteSurface((HSURF) hbmDevice); }
EngFreeMem(pdsurf); } }
return(0); }
/******************************Public*Routine******************************\
* VOID DrvDeleteDeviceBitmap * * Deletes a DFB. * \**************************************************************************/
VOID DrvDeleteDeviceBitmap( DHSURF dhsurf) { DSURF* pdsurf; PDEV* ppdev; DSURF* pTmp;
pdsurf = (DSURF*) dhsurf;
ppdev = pdsurf->ppdev;
if ((pdsurf->dt & (DT_DIB | DT_DIRECTDRAW)) == 0) { // It's a surface stashed in video memory, so we have to remove
// it from the discardable surface list:
if (ppdev->pdsurfDiscardableList == pdsurf) ppdev->pdsurfDiscardableList = pdsurf->pdsurfDiscardableNext; else { for (pTmp = ppdev->pdsurfDiscardableList; pTmp->pdsurfDiscardableNext != pdsurf; pTmp = pTmp->pdsurfDiscardableNext) ;
pTmp->pdsurfDiscardableNext = pdsurf->pdsurfDiscardableNext; } }
vVidMemFree(pdsurf); }
/******************************Public*Routine******************************\
* BOOL bAssertModeOffscreenHeap * * This function is called whenever we switch in or out of full-screen * mode. We have to convert all the off-screen bitmaps to DIBs when * we switch to full-screen (because we may be asked to draw on them even * when in full-screen, and the mode switch would probably nuke the video * memory contents anyway). * \**************************************************************************/
BOOL bAssertModeOffscreenHeap( PDEV* ppdev, BOOL bEnable) { BOOL b;
b = TRUE;
if (!bEnable) { b = bMoveAllDfbsFromOffscreenToDibs(ppdev); }
return(b); }
/******************************Public*Routine******************************\
* VOID vDisableOffscreenHeap * * Frees any resources allocated by the off-screen heap. * \**************************************************************************/
VOID vDisableOffscreenHeap( PDEV* ppdev) { SURFOBJ* psoPunt; HSURF hsurf;
psoPunt = ppdev->psoPunt; if (psoPunt != NULL) { hsurf = psoPunt->hsurf; EngUnlockSurface(psoPunt); EngDeleteSurface(hsurf); }
psoPunt = ppdev->psoPunt2; if (psoPunt != NULL) { hsurf = psoPunt->hsurf; EngUnlockSurface(psoPunt); EngDeleteSurface(hsurf); } }
/******************************Public*Routine******************************\
* BOOL bEnableOffscreenHeap * * Initializes the off-screen heap using all available video memory, * accounting for the portion taken by the visible screen. * \**************************************************************************/
BOOL bEnableOffscreenHeap( PDEV* ppdev) { SIZEL sizl; HSURF hsurf;
// Allocate a 'punt' SURFOBJ we'll use when the device-bitmap is in
// off-screen memory, but we want GDI to draw to it directly as an
// engine-managed surface:
sizl.cx = ppdev->cxMemory; sizl.cy = ppdev->cyMemory;
// We want to create it with exactly the same hooks and capabilities
// as our primary surface. We will override the 'lDelta' and 'pvScan0'
// fields later:
hsurf = (HSURF) EngCreateBitmap(sizl, 0xbadf00d, ppdev->iBitmapFormat, BMF_TOPDOWN, (VOID*) 0xbadf00d);
// We don't want GDI to turn around and call any of our Drv drawing
// functions when drawing to these surfaces, so always set the hooks
// to '0':
if ((hsurf == 0) || (!EngAssociateSurface(hsurf, ppdev->hdevEng, 0)) || (!(ppdev->psoPunt = EngLockSurface(hsurf)))) { DISPDBG((1, "Failed punt surface creation"));
EngDeleteSurface(hsurf); goto ReturnFalse; }
// We don't want GDI to turn around and call any of our Drv drawing
// functions when drawing to these surfaces, so always set the hooks
// to '0':
hsurf = (HSURF) EngCreateBitmap(sizl, 0xbadf00d, ppdev->iBitmapFormat, BMF_TOPDOWN, (VOID*) 0xbadf00d);
// We don't want GDI to call us back when drawing to these surfaces,
// so always set the hooks to '0':
if ((hsurf == 0) || (!EngAssociateSurface(hsurf, ppdev->hdevEng, 0)) || (!(ppdev->psoPunt2 = EngLockSurface(hsurf)))) { DISPDBG((1, "Failed punt surface creation"));
EngDeleteSurface(hsurf); goto ReturnFalse; }
DISPDBG((5, "Passed bEnableOffscreenHeap"));
return(TRUE);
ReturnFalse:
DISPDBG((1, "Failed bEnableOffscreenHeap"));
return(FALSE); }
|