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