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.
 
 
 
 
 
 

1668 lines
54 KiB

/******************************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 leads naturally to nifty tricks that can take advantage of
* the hardware, such as MaskBlt support and cheap double buffering
* for OpenGL.
*
* NOTE: All heap operations must be done under some sort of synchronization,
* whether it's controlled by GDI or explicitly by the driver. All
* the routines in this module assume that they have exclusive access
* to the heap data structures; multiple threads partying in here at
* the same time would be a Bad Thing. (By default, GDI does NOT
* synchronize drawing on device-created bitmaps.)
*
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved.
\*****************************************************************************/
#include "precomp.h"
#include "gdi.h"
#include "directx.h"
#include "log.h"
#include "heap.h"
#define ALLOC_TAG ALLOC_TAG_EH2P
//-----------------------------------------------------------------------------
//
// void vRemoveSurfFromList(Surf* psurf)
//
// Removes the surface from the surface list
//
//-----------------------------------------------------------------------------
VOID
vRemoveSurfFromList(PPDev ppdev, Surf* psurf)
{
DBG_GDI((3, "vRemoveSurfFromList removing psruf=0x%x", psurf));
//@@BEGIN_DDKSPLIT
ASSERTLOCK(ppdev, vRemoveSurfFromList);
//@@END_DDKSPLIT
if ( psurf != NULL && psurf->flags & SF_LIST)
{
Surf* pHead = ppdev->psurfListHead;
Surf* pTail = ppdev->psurfListTail;
if ( psurf == pHead )
{
DBG_GDI((3, "vRemoveSurfFromList removing 1st one"));
//
// Remove the first one in the list
//
Surf* pNextSurf = psurf->psurfNext;
if ( pNextSurf != NULL )
{
pNextSurf->psurfPrev = NULL;
ppdev->psurfListHead = pNextSurf;
DBG_GDI((3, "Move head to 0x%x", ppdev->psurfListHead));
}
else
{
//
// This is the only psurf in our list. Let head and tail all
// point to NULL after removal
//
DBG_GDI((3, "vRemoveSurfFromList: the only one in list"));
ppdev->psurfListHead = NULL;
ppdev->psurfListTail = NULL;
}
}// The psurf happens to be the first one in the list
else if ( psurf == pTail )
{
DBG_GDI((3, "vRemoveSurfFromList removing last one"));
//
// Remove the last one in the list
//
ppdev->psurfListTail = psurf->psurfPrev;
ppdev->psurfListTail->psurfNext = NULL;
}// The psurf happens to be the last one in the list
else
{
//
// Normal case, the psurf is in the middle of a list
//
Surf* psurfPrev = psurf->psurfPrev;
Surf* psurfNext = psurf->psurfNext;
DBG_GDI((3, "vRemoveSurfFromList removing middle one"));
psurfPrev->psurfNext = psurfNext;
psurfNext->psurfPrev = psurfPrev;
}
psurf->psurfNext = NULL;
psurf->psurfPrev = NULL;
psurf->flags &= ~SF_LIST;
}// if ( psurf != NULL )
}// vRemoveSurfFromList()
//-----------------------------------------------------------------------------
//
// void vAddSurfToList(PPDev ppdev, Surf* psurf)
//
// Adds the surface to the surface list
//
// Note: We always add the surface to the end of the list.
//
//-----------------------------------------------------------------------------
VOID
vAddSurfToList(PPDev ppdev, Surf* psurf)
{
//@@BEGIN_DDKSPLIT
ASSERTLOCK(ppdev, vAddSurfToList);
//@@END_DDKSPLIT
if ( ppdev->psurfListHead == NULL )
{
DBG_GDI((3, "vAddSurfToList add psurf=0x%x as 1st one", psurf));
//
// First time add a pdsurf to the surface list
//
ppdev->psurfListHead = psurf;
ppdev->psurfListTail = psurf;
psurf->psurfPrev = NULL;
psurf->psurfNext = NULL;
DBG_GDI((6, "vAddSurfToList set pHead as 0x%x", ppdev->psurfListHead));
}
else
{
Surf* pTail = ppdev->psurfListTail;
DBG_GDI((3, "vAddSurfToList add psurf=0x%x as the tail", psurf));
//
// Add this psurf to the end
//
pTail->psurfNext = psurf;
psurf->psurfPrev = pTail;
ppdev->psurfListTail = psurf;
DBG_GDI((6, "vAddSurfToList done: psurf->psurfPrev=0x%x",
psurf->psurfPrev));
}
psurf->flags |= SF_LIST;
}// vAddSurfToList()
//-----------------------------------------------------------------------------
//
// void vShiftSurfToListEnd(PPDev ppdev, Surf* psurf)
//
// Shifts the surface from its current position in the surface list to the
// end of surface list
//
//-----------------------------------------------------------------------------
VOID
vShiftSurfToListEnd(PPDev ppdev, Surf* psurf)
{
//@@BEGIN_DDKSPLIT
ASSERTLOCK(ppdev, vShiftSurfToListEnd);
//@@END_DDKSPLIT
Surf* pTail = ppdev->psurfListTail;
DBG_GDI((6, "vShiftSurfToListEnd psurf=0x%x, pTail=0x%x", psurf, pTail));
//
// We don't need to shift a NULL psurf or the psurf is already at the end
// of our surface list
//
if ( (psurf != NULL) && (psurf != pTail) )
{
Surf* pHead = ppdev->psurfListHead;
DBG_GDI((6, "vShiftSurfToListEnd pHead=0x%x, pTail=0x%x",
pHead, pTail));
if ( psurf == pHead )
{
//
// The surf is the first one in our list.
// So, first we shift the head and let it points to the next one
// in the list
//
ppdev->psurfListHead = psurf->psurfNext;
ppdev->psurfListHead->psurfPrev = NULL;
//
// Let the tail point to this psurf
//
pTail->psurfNext = psurf;
psurf->psurfPrev = pTail;
psurf->psurfNext = NULL;
ppdev->psurfListTail = psurf;
DBG_GDI((6,"1st shifted. New pHead=0x%x", ppdev->psurfListHead));
}// psurf is the 1st one in the list
else
{
//
// The surface is in the middle of the surface list
//
Surf* psurfPrev = psurf->psurfPrev;
Surf* psurfNext = psurf->psurfNext;
DBG_GDI((6, "vShiftSurfToListEnd psurfPrev=0x%x, psurfNext=0x%x",
psurfPrev, psurfNext));
psurfPrev->psurfNext = psurfNext;
psurfNext->psurfPrev = psurfPrev;
//
// Add this psurf to the end
//
pTail->psurfNext = psurf;
psurf->psurfPrev = pTail;
psurf->psurfNext = NULL;
ppdev->psurfListTail = psurf;
}// Normal position
}// psurf is NULL or already at the end
}// vShiftSurfToListEnd()
//-----------------------------------------------------------------------------
//
// void vSurfUsed
//
// Informs the heap manager that the surface has been accessed.
//
// Surface access patterns are the only hint that the heap manager receives
// about the usage pattern of surfaces. From this limited information, the
// heap manager must decide what surfaces to throw out of video memory when
// the amount of available video memory reaches zero.
//
// For now, we will implement a LRU algorithm by placing any accessed
// surfaces at the tail of the surface list.
//
//-----------------------------------------------------------------------------
VOID
vSurfUsed(PPDev ppdev, Surf* psurf)
{
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
if(ppdev->ulLockCount)
{
DBG_GDI((MT_LOG_LEVEL, "vSurfUsed: re-entered! %d", ppdev->ulLockCount));
}
EngAcquireSemaphore(ppdev->hsemLock);
ppdev->ulLockCount++;
#endif
//@@END_DDKSPLIT
if( psurf->flags & SF_LIST )
{
// shift any surface that we have allocated to the end of the
// list
vShiftSurfToListEnd(ppdev, psurf);
}
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
ppdev->ulLockCount--;
EngReleaseSemaphore(ppdev->hsemLock);
#endif
//@@END_DDKSPLIT
}// vSurfUsed()
//-----------------------------------------------------------------------------
//
// This function copies the bits from off-screen memory to the DIB
//
// Parameters
// ppdev-----------PPDEV
// pvSrc-----------Source pointer in the off-screen bitmap
// lBytesToUpLoad--Number of bytes to upload
// pvDst-----------Destination pointer in the DIB
//
//-----------------------------------------------------------------------------
VOID
vUpload(PPDev ppdev,
void* pvSrc,
LONG lBytesToUpLoad,
void* pvDst)
{
LONG lBytesAvailable;
DWORD srcData;
BYTE* pBitmapByte;
USHORT* pBitmapShort;
ULONG* pBitmapLong;
LONG lNumOfPixel;
PERMEDIA_DECL;
DBG_GDI((7, "vUploadRect called"));
DBG_GDI((3, "%ld bytes need to be uploaded at %x\n",
lBytesToUpLoad, pvSrc));
//@@BEGIN_DDKSPLIT
ASSERTLOCK(ppdev, vUpload);
#if MULTITHREADED && DBG
ppdev->pP2dma->ppdev = ppdev;
#endif
//@@END_DDKSPLIT
#if !defined(DMA_NOTWORKING)
if(ppdev->bGdiContext)
{
InputBufferSync(ppdev);
}
else
{
vSyncWithPermedia(ppdev->pP2dma);
}
memcpy(pvDst, pvSrc, lBytesToUpLoad);
#else
P2_DEFAULT_FB_DEPTH;
//
// Set up the relevant units correctly
// ColorDDAMode is DISABLED at initialisation time so there is no need
// to re-load it here.
//
WAIT_INPUT_FIFO(3);
SEND_TAG_DATA(LogicalOpMode, __PERMEDIA_DISABLE);
SEND_TAG_DATA(FBWriteMode, __PERMEDIA_DISABLE); // In "read" mode
SEND_TAG_DATA(FBReadMode, (permediaInfo->FBReadMode
|__FB_READ_DESTINATION
|__FB_COLOR));
//
// Enable filter mode so we can get Sync and color messages on the output
// FIFO
//
data.Word = 0;
data.FilterMode.Synchronization = __PERMEDIA_FILTER_TAG;
data.FilterMode.Color = __PERMEDIA_FILTER_DATA;
SEND_TAG_DATA(FilterMode, data.Word);
DEXE_INPUT_FIFO();
DBG_GDI((7, "pvDst = %x", pvDst));
switch ( ppdev->cPelSize )
{
case 0:
//
// Initialise current pointer
//
pBitmapByte = (BYTE*)pvDst;
lNumOfPixel = lPixelsToUpLoad;
//
// Loop to read in all the "lNumOfPixel" bytes
//
while ( lNumOfPixel > 0 )
{
//
// Get number of bytes available in the FIFO
//
WAIT_OUTPUT_FIFO_NOT_EMPTY(lBytesAvailable);
//
// Decrease the total number of bytes we need to read
//
lNumOfPixel -= lBytesAvailable;
//
// We don't want to over read. Reset "lBytesAvailable" if we
// have more available in the FIFO than we required
//
if ( lNumOfPixel < 0 )
{
lBytesAvailable += lNumOfPixel;
}
//
// Read in "lBytesAvailable" bytes
//
while ( --lBytesAvailable >= 0 )
{
READ_OUTPUT_FIFO(srcData);
*pBitmapByte++ = (BYTE)srcData;
}
}// while ( lNumOfPixel > 0 )
break;
case 1:
//
// Initialise current pointer
//
pBitmapShort = (USHORT*)pvDst;
lNumOfPixel = lPixelsToUpLoad;
while ( lNumOfPixel > 0 )
{
WAIT_OUTPUT_FIFO_NOT_EMPTY(lBytesAvailable);
lNumOfPixel -= lBytesAvailable;
if ( lNumOfPixel < 0 )
{
lBytesAvailable += lNumOfPixel;
}
while ( --lBytesAvailable >= 0 )
{
READ_OUTPUT_FIFO(srcData);
*pBitmapShort++ = (USHORT)srcData;
}
}
break;
case 2:
//
// True color mode, use DWORD as reading UNIT, here pBitmapLong
// points to the destination address, that is, the BMP data address
// in main memory
//
pBitmapLong = (ULONG*)pvDst;
lNumOfPixel = lPixelsToUpLoad;
//
// Loop until we upload all the pixels
//
while ( lNumOfPixel > 0 )
{
//
// Wait until we have something to read
//
WAIT_OUTPUT_FIFO_NOT_EMPTY(lBytesAvailable);
//
// Check here to guarntee that we don't read more than we
// asked for
//
lNumOfPixel -= lBytesAvailable;
if ( lNumOfPixel < 0 )
{
lBytesAvailable += lNumOfPixel;
}
//
// Read all these available BYTES, READ_OUTPUT_FIFO, in FIFO
// to main memory
//
while ( --lBytesAvailable >= 0 )
// while ( lBytesAvailable > 0 )
{
READ_OUTPUT_FIFO(*pBitmapLong);
++pBitmapLong;
// lBytesAvailable -= 4;
}
}
break;
}// switch ( ppdev->cPelSize )
//
// Don't bother with a WAIT_INPUT_FIFO, as we know FIFO is empty.
// We need to reset the chip back to its standard state. This
// means: enable FB writes and set the filter mode back to allow
// only syncs through.
//
WAIT_INPUT_FIFO(2);
SEND_TAG_DATA(FBWriteMode, permediaInfo->FBWriteMode);
SEND_TAG_DATA(FilterMode, 0);
EXE_INPUT_FIFO();
DBG_GDI((7, "vUploadRectNative: done"));
#endif
}// vUpload()
//---------------------------------------------------------------------------
//
// ULONG ulVidMemAllocate
//
// This function allocates "lWidth" by "lHeight" bytes of video memory
//
// Parameters:
// ppdev----------PPDEV
// lWidth---------Width of the memory to allocate
// lHeight--------Height of the memory to allocate
// lPelSize-------Pixel Size of memory chunk
// plDelta--------lDelta of this memory chunk
// ppvmHeap-------Pointer to a video memory heap, local or non-local etc.
// pulPackedPP----Packed products
// bDiscardable---TRUE if the surface can be discarded if needed
//
//--------------------------------------------------------------------------
ULONG
ulVidMemAllocate(PDev* ppdev,
LONG lWidth,
LONG lHeight,
LONG lPelSize,
LONG* plDelta,
VIDEOMEMORY** ppvmHeap,
ULONG* pulPackedPP,
BOOL bDiscardable )
{
ULONG iHeap;
VIDEOMEMORY* pvmHeap;
ULONG ulByteOffset;
LONG lDelta;
LONG lNewDelta;
ULONG packedPP;
SURFACEALIGNMENT alignment; // DDRAW heap management allignment stru
//
// Dont allocate any video memory on NT40, just let GDI do all the
// allocating.
//
if(g_bOnNT40)
return 0;
memset(&alignment, 0, sizeof(alignment));
//
// Calculate lDelta and partical products based on lWidth
// The permedia has surface width restrictions that must be met
//
vCalcPackedPP(lWidth, &lDelta, &packedPP);
lDelta <<= lPelSize;
//
// Set alignment requirements
// - must start at an pixel address
// - pitch needs to be lDelta
//
alignment.Linear.dwStartAlignment = ppdev->cjPelSize;
alignment.Linear.dwPitchAlignment = lDelta;
//
// Indicate that this allocation can be discarded if a DDraw/D3D
// app really needs the memory
//
if( bDiscardable )
{
alignment.Linear.dwFlags = SURFACEALIGN_DISCARDABLE;
}
//
// Loop through all the heaps to find available memory
// Note: This ppdev->cHeap info was set in DrvGetDirectDrawInfo
// when the driver is initialized
//
for ( iHeap = 0; iHeap < ppdev->cHeaps; iHeap++ )
{
pvmHeap = &ppdev->pvmList[iHeap];
//
// Since we are using DDRAW run time heap management code. It is
// possible that the heap hasn't been initialized. For example, if
// we fail in DrvEnableDirectDraw(), then the system won't initialize
// the heap for us
//
if ( pvmHeap == NULL )
{
DBG_GDI((1, "Video memory hasn't been initialzied"));
return 0;
}
//
// 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) )
{
//
// Ask DDRAW heap management to allocate memory for us
//
ulByteOffset = (ULONG)HeapVidMemAllocAligned(pvmHeap,
lDelta,
lHeight,
&alignment,
&lNewDelta);
DBG_GDI((3, "allocate %d bytes----got memory offset %ld real %x",
lWidth * ppdev->cjPelSize * lHeight,
ulByteOffset, (VOID*)(ppdev->pjScreen + ulByteOffset)));
if ( ulByteOffset != 0 )
{
ASSERTDD(lDelta == lNewDelta,
"ulVidMemAllocate: deltas don't match");
*ppvmHeap = pvmHeap;
*plDelta = lDelta;
*pulPackedPP = packedPP;
//
// We are done
//
return (ulByteOffset);
}// if ( pdsurf != NULL )
}// if (!(pvmHeap->dwFlags & VIDMEM_ISNONLOCAL))
}// loop through all the heaps to see if we can find available memory
return 0;
}// ulVidMemAllocate()
//---------------------------------------------------------------------------
//
// VOID vDeleteSurf(DSURF* pdsurf)
//
// This routine frees a DSURF structure and the video or system memory inside
// this DSURF
//
//----------------------------------------------------------------------------
VOID
vDeleteSurf(Surf* psurf)
{
DBG_GDI((6, "vDeleteSurf called with pdsurf =0x%x", psurf));
//
// Validate input parameters
//
if ( psurf == NULL )
{
DBG_GDI((3, "vDeleteSurf do nothing because pdsurf is NULL\n"));
return;
}
//
// Note: we don't need to call EngDeleteSurface(psurf->hsurf) to delete
// the HBITMAP we created in DrvCreateDeviceBitmap() or DrvDeriveSurface()
// because GDI will take care of this when it call DrvDeleteDeviceBitmap
//
if ( psurf->flags & SF_ALLOCATED )
{
if( psurf->flags & SF_SM )
{
ENGFREEMEM(psurf->pvScan0);
}
else
{
ASSERTDD(psurf->flags & SF_VM, "expected video memeory surface");
//
// 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:
//
psurf->ppdev->iHeapUniq++;
//
// Free the video memory by specifing which heap and the pointer
// to the chunk of video memory
//
DBG_GDI((3, "Free offset %ld from video mem\n", psurf->ulByteOffset));
VidMemFree(psurf->pvmHeap->lpHeap, (FLATPTR)psurf->ulByteOffset);
}// It is video memory
}
//
// Free the GDI wrap around video memory
//
ENGFREEMEM(psurf);
return;
}// vDeleteSurf()
//--------------------------------------------------------------------------
//
// pCreateSurf(PDEV* ppdev, LONG lWidth, LONG lHeight)
// This routine returns allocates a chunk of video memory and returns a DSURF*
//
// Parameters:
// ppdev-------PDEV*
// lWidth------Width of the bitmap to be allocated
// lHeight-----Height of the bitmap to be allocated
//
//--------------------------------------------------------------------------
Surf*
pCreateSurf(PDev* ppdev,
LONG lWidth,
LONG lHeight)
{
ULONG ulByteOffset;
Surf* psurf;
LONG lDelta;
ULONG ulPackedPP;
VIDEOMEMORY* pvmHeap;
//
// First, try to get video memory
//
ulByteOffset = ulVidMemAllocate(ppdev,
lWidth, lHeight, ppdev->cPelSize,
&lDelta, &pvmHeap, &ulPackedPP, TRUE);
if ( ulByteOffset != 0 )
{
//
// Use system memory to allocate a wrap (DSURF) so that gdi
// can track all the info later
//
psurf = (Surf*)ENGALLOCMEM(FL_ZERO_MEMORY, sizeof(Surf),
ALLOC_TAG);
if ( psurf != NULL )
{
DBG_GDI((3, "pdsurf is %x\n", psurf));
//
// Fill up the DSURF structure and our job is done
//
psurf->flags = SF_VM | SF_ALLOCATED;
psurf->ppdev = ppdev;
psurf->cx = lWidth;
psurf->cy = lHeight;
psurf->ulByteOffset = ulByteOffset;
psurf->pvmHeap = pvmHeap;
psurf->lDelta = lDelta;
psurf->ulPackedPP = ulPackedPP;
psurf->ulPixOffset = (ULONG)(ulByteOffset >> ppdev->cPelSize);
psurf->ulPixDelta = lDelta >> ppdev->cPelSize;
psurf->psurfNext = NULL;
psurf->psurfPrev = NULL;
//
// We are done
//
return(psurf);
}// if ( pdsurf != NULL )
//
// Something weird happened that we can't get memory from
// the system. We should free the video memory before quit
//
VidMemFree(pvmHeap->lpHeap, (FLATPTR)ulByteOffset);
}// if ( ulByteOffset != 0 )
return (NULL);
}// pCreateSurf()
//---------------------------------------------------------------------------
//
// BOOL bMoveOldestBMPOut
//
// This routine moves the oldest DFB in the video memory to the system memory
// and store it as a DIB
//
//---------------------------------------------------------------------------
BOOL
bMoveOldestBMPOut(PDev* ppdev)
{
BOOL bResult = FALSE;
if(ppdev->psurfListHead != NULL)
bResult = bDemote(ppdev->psurfListHead);
return bResult;
}// bMoveOldestBMPOut()
//--------------------------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).
//
// Parameters:
// dhpdev---Identifies the PDEV that describes the physical device that an
// application has designated as the primary target for a bitmap. The
// format of the bitmap must be compatible with this physical device.
// sizl-----Specifies the height and width of the desired bitmap, in pixels.
// iFormat--Specifies the bitmap format, which indicates the required number of
// bits of color information per pixel. This value can be one of the
// following: Value Meaning
// BMF_1BPP Monochrome.
// BMF_4BPP 4 bits per pixel.
// BMF_8BPP 8 bits per pixel.
// BMF_16BPP 16 bits per pixel.
// BMF_24BPP 24 bits per pixel.
// BMF_32BPP 32 bits per pixel.
// BMF_4RLE 4 bits per pixel; run length encoded.
// BMF_8RLE 8 bits per pixel; run length encoded.
//
// Return Value:
// The return value is a handle that identifies the created bitmap if the
// function is successful. If the driver chooses to let GDI create and manage
// the bitmap, the return value is zero. If an error occurs, the return value
// is 0xFFFFFFFF, and GDI logs an error code.
//
//------------------------------------------------------------------------------
HBITMAP
DrvCreateDeviceBitmap(DHPDEV dhpdev,
SIZEL sizl,
ULONG iFormat)
{
PDev* ppdev = (PDev*)dhpdev;
Surf* psurf;
HBITMAP hbmDevice = NULL;
BYTE* pjSurface;
PERMEDIA_DECL;
DBG_GDI((6, "DrvCreateDeviceBitmap()called"));
//
// First check If we're in full-screen mode ( ppdev->bEnabled = FALSE )
// If yes, we hardly have any off-screen memory in which to allocate a DFB.
// LATER: We could still allocate an OH node and put the bitmap on the DIB
// DFB list for later promotion.
// Also check that off-screen DFBs are configured ( STAT_DEV_BITMAPS). This
// flag is turned off in bCheckHighResolutionCapability() (enable.c) when
// the resolution is too high for the accelerator
//
// if ( !ppdev->bEnabled || !(ppdev->flStatus & STAT_DEV_BITMAPS) )
if ( !ppdev->bEnabled )
{
DBG_GDI((2, "DrvCreateDeviceBitmap(): return 0, full screen mode"));
return (0);
}
//
// Second check If we're in DirectDraw exclusive mode
//
if ( ppdev->bDdExclusiveMode )
{
DBG_GDI((2, "DrvCreateDeviceBitmap(): return 0, DirectDraw exclusive mode"));
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.
//
if ( iFormat != ppdev->iBitmapFormat )
{
DBG_GDI((2, "DrvCreateDeviceBitmap(): can't create bmp of format %d size(%d,%d)",
iFormat, sizl.cx, sizl.cy));
DBG_GDI((2, "only bitmaps of format %d supported!",
ppdev->iBitmapFormat));
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) )
{
DBG_GDI((2, "DrvCreateDeviceBitmap rtn 0 because BMP size is small"));
return (0);
}
else if ( ((sizl.cx >= 2048) || (sizl.cy >= 1024)) )
{
//
// On Permedia don't create anything bigger than we can rasterize
// because the rasteriser cannot handle coordinates higher than these
//
DBG_GDI((2, "DrvCreateDeviceBitmap rtn 0 for BMP too large %d %d",
sizl.cx, sizl.cy));
return (0);
}
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
if(ppdev->ulLockCount)
{
DBG_GDI((MT_LOG_LEVEL, "DrvCreateDeviceBitmap: re-entered! %d", ppdev->ulLockCount));
}
EngAcquireSemaphore(ppdev->hsemLock);
ppdev->ulLockCount++;
#endif
//@@END_DDKSPLIT
//
// Allocate a chunk of video memory for storing this bitmap
//
do
{
psurf = pCreateSurf(ppdev, sizl.cx, sizl.cy);
if ( psurf != NULL )
{
//
// Create a GDI wrap of a device managed bitmap
//
hbmDevice = EngCreateDeviceBitmap((DHSURF)psurf, sizl, iFormat);
if ( hbmDevice != NULL )
{
//
// Since 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.
//
pjSurface = psurf->ulByteOffset + ppdev->pjScreen;
DBG_GDI((3, "width=%ld pel=%ld, pjSurface=%x",
sizl.cy, ppdev->cjPelSize, pjSurface));
ULONG flags = MS_NOTSYSTEMMEMORY;
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
flags |= MS_SHAREDACCESS;
#endif
//@@END_DDKSPLIT
if ( EngModifySurface((HSURF)hbmDevice,
ppdev->hdevEng,
ppdev->flHooks,
flags,
(DHSURF)psurf,
pjSurface,
psurf->lDelta,
NULL))
{
psurf->hsurf = (HSURF)hbmDevice;
vAddSurfToList(ppdev, psurf);
DBG_GDI((6, "DrvCteDeviceBmp succeed, hsurf=%x, dsurf=%x",
hbmDevice, psurf));
vLogSurfCreated(psurf);
break;
}// if ( EngAssociateSurface() )
DBG_GDI((0, "DrvCreateDeviceBitmap,EngModifySurface failed"));
//
// Since associate surface failed, we should delete the surface
//
EngDeleteSurface((HSURF)hbmDevice);
hbmDevice = NULL;
}// if ( hbmDevice != NULL )
//
// Failed in CreateDeviceBitmap, we should free all the memory
//
vDeleteSurf(psurf);
DBG_GDI((0, "DrvCreateDeviceBitmap,EngCreateDeviceBitmap failed"));
break;
}// if ( pdsurf != NULL )
} while (bMoveOldestBMPOut(ppdev));
#if DBG
if(hbmDevice == NULL)
{
DBG_GDI((1, "DrvCreateDeviceBitmap failed, no memory"));
}
#endif
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
ppdev->ulLockCount--;
EngReleaseSemaphore(ppdev->hsemLock);
#endif
//@@END_DDKSPLIT
return (hbmDevice);
}// DrvCreateDeviceBitmap()
//--------------------------Public*Routine-------------------------------------
//
// VOID DrvDeleteDeviceBitmap()
//
// This function deletes a device bitmap created by DrvCreateDeviceBitmap
//
// Parameters
// dhsurf------Identifies the bitmap to be deleted. This handle identifies the
// bitmap created by DrvCreateDeviceBitmap.
//
// Comments
// A display driver must implement DrvDeleteDeviceBitmap if it supplies
// DrvCreateDeviceBitmap.
//
// GDI will never pass this function a DHSURF which is the same as the
// screen (Surf*)
//
//-----------------------------------------------------------------------------
VOID
DrvDeleteDeviceBitmap(DHSURF dhsurf)
{
Surf* psurf;
PDev* ppdev;
Surf* pCurrent;
psurf = (Surf*)dhsurf;
ppdev = psurf->ppdev;
DBG_GDI((6, "DrvDeleteDeviceBitamp(%lx)", psurf));
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
if(ppdev->ulLockCount)
{
DBG_GDI((MT_LOG_LEVEL, "DrvDeleteDeviceBitmap: re-entered! %d", ppdev->ulLockCount));
}
EngAcquireSemaphore(ppdev->hsemLock);
ppdev->ulLockCount++;
#endif
//@@END_DDKSPLIT
vRemoveSurfFromList(ppdev, psurf);
vLogSurfDeleted(psurf);
vDeleteSurf(psurf);
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
ppdev->ulLockCount--;
EngReleaseSemaphore(ppdev->hsemLock);
#endif
//@@END_DDKSPLIT
}// DrvDeleteDeviceBitmap()
//-----------------------------------------------------------------------------
//
// VOID vBlankScreen(PDev* ppdev)
//
// This function balnk the screen by setting the memory contents to zero
//
//-----------------------------------------------------------------------------
VOID
vBlankScreen(PDev* ppdev)
{
//
// Synchronize the hardware first
//
if( ppdev->bGdiContext )
{
InputBufferSync(ppdev);
}
else
{
#if MULTITHREADED && DBG
ppdev->pP2dma->ppdev = ppdev;
#endif
vSyncWithPermedia(ppdev->pP2dma);
}
//
// Set the video memory contents, screen portion, to zero
//
memset(ppdev->pjScreen, 0x0,
ppdev->cyScreen * ppdev->lDelta);
}// vBlankScreen()
//-----------------------------------------------------------------------------
//
// 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 bResult = TRUE;
if ( !bEnable )
{
bResult = bDemoteAll(ppdev);
//
// We need to clean the screen. bAssertModeOffscreenHeap() is called
// when DrvAssertMode(FALSE), which means we either switch to a full
// screen DOS window or this PDEV will be deleted.
//
if ( bResult )
{
vBlankScreen(ppdev);
}
}
return bResult;
}// bAssertModeOffscreenHeap()
//-----------------------------------------------------------------------------
//
// VOID vDisableOffscreenHeap
//
// Frees any resources allocated by the off-screen heap.
//
//-----------------------------------------------------------------------------
VOID
vDisableOffscreenHeap(PDev* ppdev)
{
#if 0
ASSERTDD(ppdev->psurfListHead == NULL,
"vDisableOffscreenHeap: expected surface list to be empty");
ASSERTDD(ppdev->psurfListTail == NULL,
"vDisableOffscreenHeap: expected surface list to be empty");
#endif
}// vDisableOffscreenHeap()
//-----------------------------------------------------------------------------
//
// BOOL bEnableOffscreenHeap
//
// Off-screen heap initialization
//
//-----------------------------------------------------------------------------
BOOL
bEnableOffscreenHeap(PDev* ppdev)
{
DBG_GDI((6, "bEnableOffscreenHeap called"));
ppdev->psurfListHead = NULL;
ppdev->psurfListTail = NULL;
return TRUE;
}// bEnableOffscreenHeap()
//-----------------------------------------------------------------------------
//
// BOOL bDownLoad
//
// Download a GDI owned bmp (GOB) to the video memory if we have room on the
// video off-screen heap
//
// Returns: FALSE if there wasn't room, TRUE if successfully downloaded.
//
//-----------------------------------------------------------------------------
#if defined(AFTER_BETA3)
BOOL
bDownLoad(PDev* ppdev,
Surf* psurf)
{
ULONG ulByteOffset;
LONG lDelta;
ULONG ulPackedPP;
VIDEOMEMORY* pvmHeap;
DBG_GDI((6, "bDownLoad called with psurf 0x%x", psurf));
ASSERTDD(psurf->flags & SF_SM,
"Can't move a bitmap off-screen when it's already off-screen");
if ( !(psurf->flags & SF_ALLOCATED) )
{
return (FALSE);
}
//
// If we're in full-screen mode, we can't move anything to off-screen
// memory:
//
if ( !ppdev->bEnabled )
{
return(FALSE);
}
//
// If we're in DirectDraw exclusive mode, we can't move anything to
// off-screen memory:
//
if ( ppdev->bDdExclusiveMode )
{
return(FALSE);
}
//
// Allocate video memory first
//
ulByteOffset = ulVidMemAllocate(ppdev, psurf->cx, psurf->cy, ppdev->cPelSize,
&lDelta, &pvmHeap, &ulPackedPP, TRUE);
if ( ulByteOffset == 0 )
{
//
// No more free video memory, we have to return
//
DBG_GDI((1, "No more free video memory"));
return(FALSE);
}
ULONG flags = MS_NOTSYSTEMMEMORY; // It's video-memory
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
flags |= MS_SHAREDACCESS;
#endif
//@@END_DDKSPLIT
if ( !EngModifySurface(psurf->hsurf,
ppdev->hdevEng,
ppdev->flHooks,
flags,
(DHSURF)psurf,
ulByteOffset + ppdev->pjScreen,
lDelta,
NULL))
{
//
// Failed in EngModifySurface, we should free the video memory we got
//
VidMemFree(psurf->pvmHeap->lpHeap, (FLATPTR)ulByteOffset);
return(FALSE);
}
//
// Download BMP from system memory to video memory
//
memcpy((void*)(ppdev->pjScreen + psurf->ulByteOffset),
psurf->pvScan0, lDelta * psurf->cy);
//
// Free the system memory
//
ENGFREEMEM(psurf->pvScan0);
//
// Change the attributes for this PDSURF data structures
//
psurf->flags &= ~SF_SM;
psurf->flags |= SF_VM;
psurf->ulByteOffset = ulByteOffset;
psurf->pvmHeap = pvmHeap;
psurf->lDelta = lDelta;
psurf->ulPackedPP = ulPackedPP;
psurf->ulPixOffset = (ULONG)(ulByteOffset >> ppdev->cPelSize);
psurf->ulPixDelta = lDelta >> ppdev->cPelSize;
psurf->psurfNext = NULL;
psurf->psurfPrev = NULL;
vAddSurfToList(ppdev, psurf);
return (TRUE);
}// bDownLoad()
#endif
//--------------------------Public Routine-------------------------------------
//
// HBITMAP DrvDeriveSurface
//
// This function derives and creates a GDI surface from the specified
// DirectDraw surface.
//
// Parameters
// pDirectDraw-----Points to a DD_DIRECTDRAW_GLOBAL structure that describes
// the DirectDraw object.
// pSurface--------Points to a DD_SURFACE_LOCAL structure that describes the
// DirectDraw surface around which to wrap a GDI surface.
//
// Return Value
// DrvDeriveSurface returns a handle to the created GDI surface upon success.
// It returns NULL if the call fails or if the driver cannot accelerate GDI
// drawing to the specified DirectDraw surface.
//
// Comments
// DrvDeriveSurface allows the driver to create a GDI surface around a
// DirectDraw video memory or AGP surface object in order to allow accelerated
// GDI drawing to the surface. If the driver does not hook this call, all GDI
// drawing to DirectDraw surfaces is done in software using the DIB engine.
//
// GDI calls DrvDeriveSurface with RGB surfaces only.
//
// The driver should call DrvCreateDeviceBitmap to create a GDI surface of the
// same size and format as that of the DirectDraw surface. Space for the
// actual pixels need not be allocated since it already exists.
//
//-----------------------------------------------------------------------------
HBITMAP
DrvDeriveSurface(DD_DIRECTDRAW_GLOBAL* pDirectDraw,
DD_SURFACE_LOCAL* pSurface)
{
PDev* ppdev;
Surf* psurf;
HBITMAP hbmDevice;
DD_SURFACE_GLOBAL* pSurfaceGlobal;
SIZEL sizl;
DBG_GDI((6, "DrvDeriveSurface: with pDirectDraw 0x%x, pSurface 0x%x",
pDirectDraw, pSurface));
ppdev = (PDev*)pDirectDraw->dhpdev;
pSurfaceGlobal = pSurface->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(!(pSurfaceGlobal->ddpfSurface.dwFlags & DDPF_FOURCC),
"GDI called us with a non-RGB surface!");
// The GDI driver does not accelerate surfaces in AGP memory,
// thus we fail the call
if (pSurface->ddsCaps.dwCaps & DDSCAPS_NONLOCALVIDMEM)
{
DBG_GDI((6, "DrvDeriveSurface return NULL, surface in AGP memory"));
return 0;
}
// The GDI driver does not accelerate managed surface,
// thus we fail the call
if (pSurface->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
{
DBG_GDI((6, "DrvDeriveSurface return NULL, surface is managed"));
return 0;
}
//
// 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 ( (pSurfaceGlobal->ddpfSurface.dwRGBBitCount
== (DWORD)ppdev->cjPelSize * 8) )
{
psurf = (Surf*)ENGALLOCMEM(FL_ZERO_MEMORY, sizeof(Surf), ALLOC_TAG);
if ( psurf != NULL )
{
sizl.cx = pSurfaceGlobal->wWidth;
sizl.cy = pSurfaceGlobal->wHeight;
hbmDevice = EngCreateDeviceBitmap((DHSURF)psurf,
sizl,
ppdev->iBitmapFormat);
if ( hbmDevice != NULL )
{
VOID* pvScan0;
if (pSurface->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
{
// this actually is in user memory, so don't add offset
pvScan0 = (VOID *)pSurfaceGlobal->fpVidMem;
}
else
{
pvScan0 = ppdev->pjScreen + pSurfaceGlobal->fpVidMem;
}
//
// Note that HOOK_SYNCHRONIZE must always be hooked when we
// give GDI a pointer to the bitmap bits. We don't need to
// do it here since HOOK_SYNCHRONIZE is always set in our
// pdev->flHooks
//
ULONG flags = MS_NOTSYSTEMMEMORY;
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
flags |= MS_SHAREDACCESS;
#endif
//@@END_DDKSPLIT
if ( EngModifySurface((HSURF)hbmDevice,
ppdev->hdevEng,
ppdev->flHooks,
flags,
(DHSURF)psurf,
pvScan0,
pSurfaceGlobal->lPitch,
NULL) )
{
ULONG ulPackedPP;
LONG lDelta;
psurf->hsurf = (HSURF)hbmDevice;
psurf->flags = SF_DIRECTDRAW | SF_VM;
psurf->ppdev = ppdev;
psurf->cx = pSurfaceGlobal->wWidth;
psurf->cy = pSurfaceGlobal->wHeight;
psurf->ulByteOffset= (ULONG)(pSurfaceGlobal->fpVidMem);
psurf->pvmHeap = pSurfaceGlobal->lpVidMemHeap;
psurf->psurfNext = NULL;
psurf->psurfPrev = NULL;
psurf->lDelta = pSurfaceGlobal->lPitch;
vCalcPackedPP(psurf->cx, &lDelta, &ulPackedPP);
psurf->ulPackedPP = ulPackedPP;
psurf->ulPixOffset = (ULONG)(psurf->ulByteOffset
>> ppdev->cPelSize);
psurf->ulPixDelta = psurf->lDelta
>> ppdev->cPelSize;
DBG_GDI((6, "DrvDeriveSurface return succeed"));
vLogSurfCreated(psurf);
if(MAKE_BITMAPS_OPAQUE)
{
SURFOBJ* surfobj = EngLockSurface((HSURF) hbmDevice);
ASSERTDD(surfobj->iType == STYPE_BITMAP,
"expected STYPE_BITMAP");
surfobj->iType = STYPE_DEVBITMAP;
EngUnlockSurface(surfobj);
}
return(hbmDevice);
}// EngModifySurface succeed
DBG_GDI((0, "DrvDeriveSurface: EngModifySurface failed"));
EngDeleteSurface((HSURF)hbmDevice);
}
DBG_GDI((0, "DrvDeriveSurface: EngAllocMem failed"));
ENGFREEMEM(psurf);
}// if ( pdsurf != NULL )
}// Check surface format
DBG_GDI((6, "DrvDeriveSurface return NULL"));
DBG_GDI((6,"pSurfaceGlobal->ddpfSurface.dwRGBBitCount = %d, lPitch =%ld",
pSurfaceGlobal->ddpfSurface.dwRGBBitCount,pSurfaceGlobal->lPitch));
DBG_GDI((6, "ppdev->cjPelSize * 8 = %d, lDelta =%d",
ppdev->cjPelSize * 8, ppdev->lDelta));
return(0);
}// DrvDeriveSurface()
//-----------------------------------------------------------------------------
//
// VOID vDemote
//
// Attempt to move the given surface from VM to SM
//
//-----------------------------------------------------------------------------
BOOL
bDemote(Surf * psurf)
{
LONG lDelta;
VOID* pvScan0;
BOOL bResult = FALSE;
ASSERTDD( psurf->flags & SF_VM, "source to be VM");
ASSERTDD( psurf->flags & SF_ALLOCATED, "source must have been allocated");
//
// Make the system-memory scans quadword aligned:
//
lDelta = (psurf->lDelta + 7) & ~7;
DBG_GDI((7, "Allocate %ld bytes in Eng, lDelta=%ld\n",
lDelta * psurf->cy, lDelta));
//
// Allocate system memory to hold the bitmap
// Note: there's no point in zero-initializing this memory:
//
pvScan0 = ENGALLOCMEM(0, lDelta * psurf->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(psurf->hsurf,
psurf->ppdev->hdevEng,
HOOK_COPYBITS | HOOK_BITBLT,
0, // It's system-memory
(DHSURF)psurf,
pvScan0,
lDelta,
NULL))
{
//
// First, copy the bits from off-screen memory to the DIB
//
DBG_GDI((3, "Free %d bytes, offset %ld real %x",
lDelta * psurf->cy, psurf->ulByteOffset,
(VOID*)(psurf->ppdev->pjScreen + psurf->ulByteOffset)));
vUpload(psurf->ppdev, (void*)(psurf->ppdev->pjScreen + psurf->ulByteOffset),
lDelta * psurf->cy, pvScan0);
DBG_GDI((6, "bMoveOldest() free vidmem %ld",
psurf->ulByteOffset));
//
// Now free the off-screen memory:
//
VidMemFree(psurf->pvmHeap->lpHeap,
(FLATPTR)psurf->ulByteOffset);
vRemoveSurfFromList(psurf->ppdev, psurf);
//
// Setup the pdsurf properly because it is a DIB now
//
psurf->flags &= ~SF_VM;
psurf->flags |= SF_SM;
psurf->pvScan0 = pvScan0;
vLogSurfMovedToSM(psurf);
bResult = TRUE;
}// EngModifySurface()
else
{
//
// Somehow, EngModifySurface() failed. Free the memory
//
ENGFREEMEM(pvScan0);
ASSERTDD(0, "bMoveOldest() EngModifySurface failed\n");
}
}// if ( pvScan0 != NULL )
return bResult;
}
//-----------------------------------------------------------------------------
//
// VOID vPromote
//
// Attempt to move the given surface from SM to VM
//
//-----------------------------------------------------------------------------
VOID
vPromote(Surf * psurf)
{
ASSERTDD( psurf->flags & SF_VM, "source to be VM");
ASSERTDD( psurf->flags & SF_ALLOCATED, "source must have been allocated");
ASSERTDD(!psurf->ppdev->bDdExclusiveMode,
"cannot promote when DirectDraw is in exclusive mode");
// nothing for now
}
//-----------------------------------------------------------------------------
//
// BOOL bDemoteAll
//
// Attempts to move all surfaces to SM
//
//-----------------------------------------------------------------------------
BOOL
bDemoteAll(PPDev ppdev)
{
BOOL bRet;
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
EngAcquireSemaphore(ppdev->hsemLock);
ppdev->ulLockCount++;
#endif
//@@END_DDKSPLIT
while (ppdev->psurfListHead != NULL)
if(!bDemote(ppdev->psurfListHead))
break;
bRet = (ppdev->psurfListHead == NULL);
//@@BEGIN_DDKSPLIT
#if MULTITHREADED
ppdev->ulLockCount--;
EngReleaseSemaphore(ppdev->hsemLock);
#endif
//@@END_DDKSPLIT
return bRet;
}