windows-nt/Source/XPSP1/NT/drivers/video/ms/s3/disp/heap.c
2020-09-26 16:20:57 +08:00

677 lines
22 KiB
C

/******************************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);
}