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