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.
 
 
 
 
 
 

1312 lines
44 KiB

/******************************Module*Header**********************************\
*
* *******************
* * D3D SAMPLE CODE *
* *******************
*
* Module Name: d3dbuff.c
*
//@@BEGIN_DDKSPLIT
* Content: Main context callbacks for D3D videomemory buffers
//@@END_DDKSPLIT
*
* Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-2003 Microsoft Corporation. All rights reserved.
\*****************************************************************************/
#include "glint.h"
#include "dma.h"
#include "tag.h"
//@@BEGIN_DDKSPLIT
#if DX7_VERTEXBUFFERS
#define _32_KBYTES ( 32 * 1024 )
//-----------------------------------------------------------------------------
// in-the-file nonexported forward declarations
//-----------------------------------------------------------------------------
BOOL __EB_RemoveFromBufferQueue(P3_THUNKEDDATA* pThisDisplay,
P3_VERTEXBUFFERINFO* pVictim);
void __EB_Wait(P3_THUNKEDDATA* pThisDisplay, P3_VERTEXBUFFERINFO* pBuffer);
#if DBG
//-----------------------------------------------------------------------------
//
// __EB_DisplayHeapInfo
//
// A debug function. Displays the current buffer queue
//
//-----------------------------------------------------------------------------
void
__EB_DisplayHeapInfo(
int DebugLevel,
P3_THUNKEDDATA* pThisDisplay)
{
DWORD dwSequenceID = 0xFFFFFFFF;
if (DebugLevel <= P3R3DX_DebugLevel)
{
P3_VERTEXBUFFERINFO* pCurrentCommandBuffer =
pThisDisplay->pRootCommandBuffer;
P3_VERTEXBUFFERINFO* pCurrentVertexBuffer =
pThisDisplay->pRootVertexBuffer;
int count = 0;
DISPDBG((DebugLevel,"Command buffer heap"));
dwSequenceID = 0xFFFFFFFF;
if (pCurrentCommandBuffer)
{
do
{
// A debug check to ensure that the sequence ID's go backwards
ASSERTDD((dwSequenceID >= pCurrentCommandBuffer->dwSequenceID),
"ERROR: Sequence ID's broken!");
DISPDBG((DebugLevel,"Buffer %d,SequenceID:0x%x Pointer: 0x%x",
count++,
pCurrentCommandBuffer->dwSequenceID,
pCurrentCommandBuffer));
DISPDBG((DebugLevel," pPrev: 0x%x, pNext: 0x%x",
pCurrentCommandBuffer->pPrev,
pCurrentCommandBuffer->pNext));
DISPDBG((DebugLevel," bInUse: %d",
pCurrentCommandBuffer->bInUse));
dwSequenceID = pCurrentCommandBuffer->dwSequenceID;
pCurrentCommandBuffer = pCurrentCommandBuffer->pNext;
} while (pCurrentCommandBuffer != pThisDisplay->pRootCommandBuffer);
}
DISPDBG((DebugLevel,"Vertex buffer heap"));
dwSequenceID = 0xFFFFFFFF;
if (pCurrentVertexBuffer)
{
do
{
// A debug check to ensure that the sequence ID's go backwards
ASSERTDD((dwSequenceID >= pCurrentVertexBuffer->dwSequenceID),
"ERROR: Sequence ID's broken!");
DISPDBG((DebugLevel,"Buffer %d,SequenceID:0x%x Pointer: 0x%x",
count++,
pCurrentVertexBuffer->dwSequenceID,
pCurrentVertexBuffer));
DISPDBG((DebugLevel," pPrev: 0x%x, pNext: 0x%x",
pCurrentVertexBuffer->pPrev,
pCurrentVertexBuffer->pNext));
DISPDBG((DebugLevel," bInUse: %d",
pCurrentVertexBuffer->bInUse));
dwSequenceID = pCurrentVertexBuffer->dwSequenceID;
pCurrentVertexBuffer = pCurrentVertexBuffer->pNext;
} while (pCurrentVertexBuffer != pThisDisplay->pRootVertexBuffer);
}
}
} // __EB_DisplayHeapInfo
#endif // DBG
//-----------------------------------------------------------------------------
//
// __EB_FreeCachedBuffer
//
// Frees a buffer associated with this directdraw surface. This is called
// in response to a destroyexecutebuffer call
//
//-----------------------------------------------------------------------------
void
__EB_FreeCachedBuffer(
P3_THUNKEDDATA* pThisDisplay,
LPDDRAWI_DDRAWSURFACE_LCL pSurf)
{
P3_VERTEXBUFFERINFO* pVictim =
(P3_VERTEXBUFFERINFO*)pSurf->lpGbl->dwReserved1;
if (pVictim != NULL)
{
DISPDBG((DBGLVL,"Buffer is one of ours - destroying it"));
// Wait for the buffer to be consumed
__EB_Wait(pThisDisplay, pVictim);
// Set the inuse flag off so the buffer will be freed.
pVictim->bInUse = FALSE;
// Remove the buffer from the pending list
// The memory won't be freed if the buffer isn't in the list
if (!__EB_RemoveFromBufferQueue(pThisDisplay, pVictim))
{
// Free the memory
switch (pVictim->BufferType)
{
case COMMAND_BUFFER:
_DX_LIN_FreeLinearMemory(&pThisDisplay->CachedCommandHeapInfo,
PtrToUlong(pVictim));
break;
case VERTEX_BUFFER:
_DX_LIN_FreeLinearMemory(&pThisDisplay->CachedVertexHeapInfo,
PtrToUlong(pVictim));
break;
}
}
// Reset the buffer pointers
pSurf->lpGbl->dwReserved1 = 0;
pSurf->lpGbl->fpVidMem = 0;
}
} // __EB_FreeCachedBuffer
//-----------------------------------------------------------------------------
//
// __EB_GetSequenceID
//
// Each vertex buffer and command buffer is "stamped" with a sequence ID which
// can be queried in order to find out if a given buffer was already consumed
// by the hardware. __EB_GetSequenceID returns us the sequence ID of the last
// processed buffer.
//
//-----------------------------------------------------------------------------
const DWORD
__EB_GetSequenceID(
P3_THUNKEDDATA* pThisDisplay)
{
DWORD dwSequenceID;
dwSequenceID = READ_GLINT_CTRL_REG(HostInID);
DISPDBG((DBGLVL,"HostIn ID: 0x%x", dwSequenceID));
return dwSequenceID;
} // __EB_GetSequenceID
//-----------------------------------------------------------------------------
//
// __EB_GetNewSequenceID
//
// A driver routine to increment the sequence ID and return it. This
// routine handles the case where the buffer is wrapped beyond the maximum
// number that it can hold. In such case all buffers are flushed
//
//-----------------------------------------------------------------------------
const DWORD
__EB_GetNewSequenceID(
P3_THUNKEDDATA* pThisDisplay)
{
DWORD dwWrapMask;
DBG_ENTRY(__EB_GetNewSequenceID);
#if DBG
dwWrapMask = 0x1F;
#else
dwWrapMask = 0xFFFFFFFF;
#endif
if( pThisDisplay->dwCurrentSequenceID >= dwWrapMask )
{
// We have wrapped, so flush all the buffers
// but wait for them to be consumed (bWait == TRUE)
_D3D_EB_FlushAllBuffers(pThisDisplay , TRUE);
// This SYNC is needed for unknown reasons - further investigation
// required. //azn???
SYNC_WITH_GLINT;
// Reset the sequence ID numbering
pThisDisplay->dwCurrentSequenceID = 0;
}
else
{
pThisDisplay->dwCurrentSequenceID++;
}
DISPDBG((DBGLVL, "Returning Sequence ID: 0x%x",
pThisDisplay->dwCurrentSequenceID));
DBG_EXIT(__EB_GetNewSequenceID,pThisDisplay->dwCurrentSequenceID);
return pThisDisplay->dwCurrentSequenceID;
} // __EB_GetNewSequenceID
//-----------------------------------------------------------------------------
//
// __EB_Wait
//
// If the current buffer is in the queue, wait for it to pass through
// the chip.
//
//-----------------------------------------------------------------------------
void
__EB_Wait(
P3_THUNKEDDATA* pThisDisplay,
P3_VERTEXBUFFERINFO* pBuffer)
{
DBG_ENTRY(__EB_Wait);
ASSERTDD(pBuffer, "ERROR: Buffer passed to __EB_Wait is null!");
// Don't wait for the buffer, if it has not been added to the queue
if (pBuffer->pNext != NULL)
{
// Flush to ensure that the hostin ID has been sent to the chip
P3_DMA_DEFS();
P3_DMA_GET_BUFFER();
P3_DMA_FLUSH_BUFFER();
DISPDBG((DBGLVL, "*** In __EB_Wait: Buffer Sequence ID: 0x%x",
pBuffer->dwSequenceID));
while (__EB_GetSequenceID(pThisDisplay) < pBuffer->dwSequenceID)
{
static int blockCount;
// This buffer is in the chain of buffers that are being used
// by the host-in unit. We must wait for it to be consumed
// before freeing it.
blockCount = 100;
while( blockCount-- )
NULL;
}
}
DBG_EXIT(__EB_Wait,0);
} // __EB_Wait
//-----------------------------------------------------------------------------
//
// __EB_RemoveFromBufferQueue
//
// Removes a buffer from the queue. Will also free the associated memory
// if it is no longer in use
//
//-----------------------------------------------------------------------------
BOOL
__EB_RemoveFromBufferQueue(
P3_THUNKEDDATA* pThisDisplay,
P3_VERTEXBUFFERINFO* pVictim)
{
ASSERTDD(pVictim != NULL,
"ERROR: NULL buffer passed to EB_RemoveFromList");
DBG_ENTRY(__EB_RemoveFromBufferQueue);
// Don't remove a buffer that isn't already in the queue
if (pVictim->pNext == NULL)
{
DBG_EXIT(__EB_RemoveFromBufferQueue,FALSE);
return FALSE;
}
DISPDBG((DBGLVL,"Removing buffer for queue, ID: 0x%x",
pVictim->dwSequenceID));
// Remove this entry from the list
pVictim->pPrev->pNext = pVictim->pNext;
pVictim->pNext->pPrev = pVictim->pPrev;
switch (pVictim->BufferType)
{
case COMMAND_BUFFER:
// Replace the root node if necessary
if (pVictim == pThisDisplay->pRootCommandBuffer)
{
if (pVictim->pNext != pThisDisplay->pRootCommandBuffer)
{
pThisDisplay->pRootCommandBuffer = pVictim->pNext;
}
else
{
pThisDisplay->pRootCommandBuffer = NULL;
}
}
break;
case VERTEX_BUFFER:
// Replace the root node if necessary
if (pVictim == pThisDisplay->pRootVertexBuffer)
{
if (pVictim->pNext != pThisDisplay->pRootVertexBuffer)
{
pThisDisplay->pRootVertexBuffer = pVictim->pNext;
}
else
{
pThisDisplay->pRootVertexBuffer = NULL;
}
}
break;
} // switch (pVictim->BufferType)
// Buffer is no longer in the list
pVictim->pPrev = NULL;
pVictim->pNext = NULL;
// Free the memory we found if it isn't reserved as a real buffer.
if (!pVictim->bInUse)
{
DISPDBG((DBGLVL," Buffer is old - freeing the memory"));
switch (pVictim->BufferType)
{
case COMMAND_BUFFER:
_DX_LIN_FreeLinearMemory(&pThisDisplay->CachedCommandHeapInfo,
PtrToUlong(pVictim));
break;
case VERTEX_BUFFER:
_DX_LIN_FreeLinearMemory(&pThisDisplay->CachedVertexHeapInfo,
PtrToUlong(pVictim));
break;
}
DBG_EXIT(__EB_RemoveFromBufferQueue,TRUE);
return TRUE;
}
DBG_EXIT(__EB_RemoveFromBufferQueue,FALSE);
return FALSE;
} // __EB_RemoveFromBufferQueue
//-----------------------------------------------------------------------------
//
// __EB_AddToBufferQueue
//
// Adds a buffer to the queue. Note that buffers are always added
// at the start to maintain a temporal ordering of the buffers.
//
//-----------------------------------------------------------------------------
void
__EB_AddToBufferQueue(
P3_THUNKEDDATA* pThisDisplay,
P3_VERTEXBUFFERINFO* pNewBuffer)
{
DBG_ENTRY(__EB_AddToBufferQueue);
ASSERTDD(pNewBuffer != NULL,
"ERROR: NULL buffer passed to EB_AddToList");
ASSERTDD(pNewBuffer->pNext == NULL,
"ERROR: Buffer already in queue (pNext!NULL)");
ASSERTDD(pNewBuffer->pPrev == NULL,
"ERROR: Buffer already in queue (pPrev!NULL)");
switch(pNewBuffer->BufferType)
{
case COMMAND_BUFFER:
// Add the buffer to the queue
// Check that the queue isn't empty.
// If it is start a new list
if (pThisDisplay->pRootCommandBuffer == NULL)
{
DISPDBG((DBGLVL,"Command Buffer queue is empty."
" Starting a new one"));
// Sew in the buffer
pThisDisplay->pRootCommandBuffer = pNewBuffer;
pNewBuffer->pNext = pNewBuffer;
pNewBuffer->pPrev = pNewBuffer;
}
else
{
DISPDBG((DBGLVL,"Adding command buffer to the list"));
// Always put new buffers at the root.
pNewBuffer->pNext = pThisDisplay->pRootCommandBuffer;
pNewBuffer->pPrev = pThisDisplay->pRootCommandBuffer->pPrev;
pThisDisplay->pRootCommandBuffer->pPrev->pNext = pNewBuffer;
pThisDisplay->pRootCommandBuffer->pPrev = pNewBuffer;
pThisDisplay->pRootCommandBuffer = pNewBuffer;
}
break;
case VERTEX_BUFFER:
// Add the buffer to the queue
// Check that the queue isn't empty. If it is start a new list
if (pThisDisplay->pRootVertexBuffer == NULL)
{
DISPDBG((DBGLVL,"Vertex Buffer queue is empty. Starting a new one"));
// Sew in the buffer
pThisDisplay->pRootVertexBuffer = pNewBuffer;
pNewBuffer->pNext = pNewBuffer;
pNewBuffer->pPrev = pNewBuffer;
}
else
{
DISPDBG((DBGLVL,"Adding vertex buffer to the list"));
// Always put new buffers at the root.
pNewBuffer->pNext = pThisDisplay->pRootVertexBuffer;
pNewBuffer->pPrev = pThisDisplay->pRootVertexBuffer->pPrev;
pThisDisplay->pRootVertexBuffer->pPrev->pNext = pNewBuffer;
pThisDisplay->pRootVertexBuffer->pPrev = pNewBuffer;
pThisDisplay->pRootVertexBuffer = pNewBuffer;
}
break;
} // switch(pNewBuffer->BufferType)
DISPDBG((DBGLVL, "Added buffer to queue, ID: 0x%x", pNewBuffer->dwSequenceID));
DBG_EXIT(__EB_AddToBufferQueue,pNewBuffer->dwSequenceID);
} // __EB_AddToBufferQueue
//-----------------------------------------------------------------------------
//
// __EB_AllocateCachedBuffer
//
// Allocates a cached buffer and stores it in the surface structure.
// First this function will try to allocate out of the linear heap.
// If this fails, it will keep walking the buffer queue until there
// are no more buffers left that are pending and aren't in use.
// If all else fails this driver will return FALSE indicating that
// it couldn't allocate the memory due to lack of space
//
//-----------------------------------------------------------------------------
BOOL
__EB_AllocateCachedBuffer(
P3_THUNKEDDATA* pThisDisplay,
DWORD dwBytes,
LPDDRAWI_DDRAWSURFACE_LCL pSurf)
{
P3_MEMREQUEST mmrq;
DWORD dwResult;
P3_VERTEXBUFFERINFO* pCurrentBuffer;
P3_VERTEXBUFFERINFO** ppRootBuffer;
BOOL bFound;
eBufferType BufferType;
pLinearAllocatorInfo pAllocHeap;
static int blockCount;
DBG_ENTRY(__EB_AllocateCachedBuffer);
#if WNT_DDRAW
pAllocHeap = &pThisDisplay->CachedVertexHeapInfo;
BufferType = VERTEX_BUFFER;
ppRootBuffer = &pThisDisplay->pRootVertexBuffer;
#else
// Decide on which heap this surface should come out of.
if (pSurf->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_COMMANDBUFFER)
{
pAllocHeap = &pThisDisplay->CachedCommandHeapInfo;
BufferType = COMMAND_BUFFER;
ppRootBuffer = &pThisDisplay->pRootCommandBuffer;
DISPDBG((DBGLVL,"Buffer is COMMAND_BUFFER"));
}
else
{
pAllocHeap = &pThisDisplay->CachedVertexHeapInfo;
BufferType = VERTEX_BUFFER;
ppRootBuffer = &pThisDisplay->pRootVertexBuffer;
DISPDBG((DBGLVL,"Buffer is VERTEX_BUFFER"));
}
#endif // WNT_DDRAW
#if DBG
// Dump the memory and the pending buffer heaps.
__EB_DisplayHeapInfo(DBGLVL, pThisDisplay);
#endif //DBG
// Do a quick check to see if the buffer at the back is free.
if ((*ppRootBuffer) != NULL)
{
pCurrentBuffer = (*ppRootBuffer)->pPrev;
// If the buffer is big enough, and it's completed, and
// it isn't in use, then free it.
if ( ((dwBytes + sizeof(P3_VERTEXBUFFERINFO)) <= pCurrentBuffer->dwSize) &&
(!pCurrentBuffer->bInUse) &&
(__EB_GetSequenceID(pThisDisplay) >= pCurrentBuffer->dwSequenceID) )
{
// Mark this buffer as in use, so that it doesn't get freed
pCurrentBuffer->bInUse = TRUE;
// It isn't pending any more, so remove it from the queue
// Note that the memory won't be deallocated because we have explicity
// marked it as in use.
__EB_RemoveFromBufferQueue(pThisDisplay, pCurrentBuffer);
// Pass back a pointer to the memory
pSurf->lpGbl->fpVidMem = (FLATPTR)((BYTE *)pCurrentBuffer) +
sizeof(P3_VERTEXBUFFERINFO);
// Store a pointer to the info block at the start of the memory
pSurf->lpGbl->dwReserved1 = (ULONG_PTR)pCurrentBuffer;
#if W95_DDRAW
// Setup the caps
pSurf->lpGbl->dwGlobalFlags |= DDRAWISURFGBL_SYSMEMEXECUTEBUFFER;
#endif
// If you set these you don't see any locks....
pSurf->ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
// Remember the new size
pSurf->lpGbl->dwLinearSize = dwBytes;
// Mark the buffer as in use and return it
pCurrentBuffer->dwSequenceID = 0;
pCurrentBuffer->bInUse = TRUE;
pCurrentBuffer->pNext = NULL;
pCurrentBuffer->pPrev = NULL;
pCurrentBuffer->BufferType = BufferType;
pCurrentBuffer->dwSize = dwBytes + sizeof(P3_VERTEXBUFFERINFO);
DISPDBG((DBGLVL,"Found a re-useable buffer "
"- didn't need to reallocate memory"));
DBG_EXIT(__EB_AllocateCachedBuffer,TRUE);
return TRUE;
}
}
// do things the longer way...
// Try to allocate the requested memory
do
{
ZeroMemory(&mmrq, sizeof(P3_MEMREQUEST));
mmrq.dwSize = sizeof(P3_MEMREQUEST);
mmrq.dwAlign = 4;
mmrq.dwBytes = dwBytes + sizeof(P3_VERTEXBUFFERINFO);
mmrq.dwFlags = MEM3DL_FIRST_FIT | MEM3DL_FRONT;
dwResult = _DX_LIN_AllocateLinearMemory(pAllocHeap, &mmrq);
if (dwResult == GLDD_SUCCESS)
{
DISPDBG((DBGLVL,"Allocated a cached buffer"));
// Pass back a pointer to the memory
pSurf->lpGbl->fpVidMem = mmrq.pMem + sizeof(P3_VERTEXBUFFERINFO);
// Store a pointer to the info block at the start of the memory
pSurf->lpGbl->dwReserved1 = mmrq.pMem;
#if W95_DDRAW
// Setup the caps
pSurf->lpGbl->dwGlobalFlags |= DDRAWISURFGBL_SYSMEMEXECUTEBUFFER;
#endif
// If you set these you don't see any locks....
pSurf->ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
// Remember the new size
pSurf->lpGbl->dwLinearSize = dwBytes;
// Mark the buffer as in use and return it
pCurrentBuffer = (P3_VERTEXBUFFERINFO*)(ULONG_PTR)mmrq.pMem;
pCurrentBuffer->dwSequenceID = 0;
pCurrentBuffer->bInUse = TRUE;
pCurrentBuffer->pNext = NULL;
pCurrentBuffer->pPrev = NULL;
pCurrentBuffer->BufferType = BufferType;
pCurrentBuffer->dwSize = dwBytes + sizeof(P3_VERTEXBUFFERINFO);
DBG_EXIT(__EB_AllocateCachedBuffer,TRUE);
return TRUE;
}
else
{
DISPDBG((DBGLVL,"Failed to allocate cached buffer"));
// Remember that we haven't found any memory yet.
// and that there isn't any memory to use.
bFound = FALSE;
// There are no buffers currently available.
// Wait for a new one to be free from the
// ones that are available and in the queue
// None at all? No chance of any memory being free
// Return FALSE to indicate this.
if ((*ppRootBuffer) == NULL)
{
DISPDBG((DBGLVL,"No buffers in the list!"));
DBG_EXIT(__EB_AllocateCachedBuffer,FALSE);
return FALSE;
}
// Start at the back of the queue, as these are
// the least recently used buffers
pCurrentBuffer = (*ppRootBuffer)->pPrev;
do
{
P3_DMA_DEFS();
// Ensure that all DMA is completed so that the HostIn
// ID is up to date
P3_DMA_GET_BUFFER();
P3_DMA_FLUSH_BUFFER();
DISPDBG((DBGLVL,"Searching for old buffers..."));
// Check to see if this buffer is available to be freed
// It may not be if it is a buffer that hasn't been swapped out,
// such as a vertex buffer.
DISPDBG((DBGLVL,"This buffer ID: 0x%x",
pCurrentBuffer->dwSequenceID));
if( __EB_GetSequenceID(pThisDisplay) >=
pCurrentBuffer->dwSequenceID )
{
DISPDBG((DBGLVL,"Found a buffer that can be "
"removed from the list"));
// It isn't pending any more, so remove it from the queue
if (__EB_RemoveFromBufferQueue(pThisDisplay, pCurrentBuffer))
{
bFound = TRUE;
break;
}
// If the queue is gone, exit (bFound hasn't been
// setup because we didn't find any memory in the queue)
if ((*ppRootBuffer) == NULL)
{
break;
}
// Reset to the last buffer in the chain. This ensures that
// we always look at the least recent buffer
pCurrentBuffer = (*ppRootBuffer)->pPrev;
}
else
{
// BLOCK!
// The buffer we are looking at hasn't become available yet.
// We should back off here until it does.
blockCount = 100;
while( blockCount-- )
NULL;
DISPDBG((DBGLVL,"Blocked waiting for buffer to be free"));
}
} while (pCurrentBuffer != NULL);
}
// Loop until we haven't found any more space to allocate buffers into
} while (bFound);
DISPDBG((WRNLVL,"!! No available new buffers pending to be freed!!"));
DBG_EXIT(__EB_AllocateCachedBuffer,FALSE);
return FALSE;
} // __EB_AllocateCachedBuffer
//-----------------------------------------------------------------------------
//
// _D3D_EB_FlushAllBuffers
//
// Empties the queue. Note that this will cause any allocated buffer
// memory to be freed along the way. This version doesn't wait for the
// buffer to be consumed. It is used when a context switch has
// occured and we know it is safe to do
//
//-----------------------------------------------------------------------------
void
_D3D_EB_FlushAllBuffers(
P3_THUNKEDDATA* pThisDisplay,
BOOL bWait)
{
P3_VERTEXBUFFERINFO* pCurrentBuffer;
DBG_ENTRY(_D3D_EB_FlushAllBuffers);
// Walk the list of buffers, flushing them from the queue
while (pThisDisplay->pRootVertexBuffer != NULL)
{
pCurrentBuffer = pThisDisplay->pRootVertexBuffer;
if(bWait)
{
// Wait for the buffer to be consumed
__EB_Wait(pThisDisplay, pCurrentBuffer);
}
// Remove the buffer from the queue
__EB_RemoveFromBufferQueue(pThisDisplay, pCurrentBuffer);
}
while (pThisDisplay->pRootCommandBuffer != NULL)
{
pCurrentBuffer = pThisDisplay->pRootCommandBuffer;
if(bWait)
{
// Wait for the buffer to be consumed
__EB_Wait(pThisDisplay, pCurrentBuffer);
}
// Remove the buffer from the queue
__EB_RemoveFromBufferQueue(pThisDisplay, pCurrentBuffer);
}
DBG_EXIT(_D3D_EB_FlushAllBuffers,0);
} // _D3D_EB_FlushAllBuffers
//-----------------------------------------------------------------------------
//
// _D3D_EB_GetAndWaitForBuffers
//
//-----------------------------------------------------------------------------
void
_D3D_EB_GetAndWaitForBuffers(
P3_THUNKEDDATA* pThisDisplay,
LPD3DHAL_DRAWPRIMITIVES2DATA pdp2d ,
P3_VERTEXBUFFERINFO** ppVertexBufferInfo,
P3_VERTEXBUFFERINFO** ppCommandBufferInfo)
{
P3_VERTEXBUFFERINFO* pVertexBufferInfo;
P3_VERTEXBUFFERINFO* pCommandBufferInfo;
pCommandBufferInfo =
(P3_VERTEXBUFFERINFO*)pdp2d->lpDDCommands->lpGbl->dwReserved1;
// Check if vertex buffer resides in user memory or in a DDraw surface
if (pdp2d->dwFlags & D3DHALDP2_USERMEMVERTICES)
{
pVertexBufferInfo = NULL;
}
else
{
// This pointer may be NULL, indicating a buffer passed that isn't
// one of ours. That doesn't mean to say that we can't swap in one
// of our buffers if there is one available.
pVertexBufferInfo =
(P3_VERTEXBUFFERINFO*)pdp2d->lpDDVertex->lpGbl->dwReserved1;
}
// If the vertex buffer is in the queue, wait for it.
if (pVertexBufferInfo && pVertexBufferInfo->pPrev)
{
// Wait for this buffer if we need to
__EB_Wait(pThisDisplay, pVertexBufferInfo);
// Remove this buffer from the queue
if (__EB_RemoveFromBufferQueue(pThisDisplay, pVertexBufferInfo))
{
DISPDBG((ERRLVL,"ERROR: This buffer should not have been freed "
"(is in use!)"));
}
}
// If the command buffer is in the queue, wait for it.
if (pCommandBufferInfo && pCommandBufferInfo->pPrev)
{
// Wait for this buffer if we need to
__EB_Wait(pThisDisplay, pCommandBufferInfo);
// Remove this buffer from the queue
if (__EB_RemoveFromBufferQueue(pThisDisplay, pCommandBufferInfo))
{
DISPDBG((ERRLVL,"ERROR: This buffer should not have been freed"
" (is in use!)"));
}
}
// Return current values of pointers to EB buffers
*ppCommandBufferInfo = pCommandBufferInfo;
*ppVertexBufferInfo = pVertexBufferInfo;
} // _D3D_EB_GetAndWaitForBuffers
//-----------------------------------------------------------------------------
//
// _D3D_EB_UpdateSwapBuffers
//
//-----------------------------------------------------------------------------
void
_D3D_EB_UpdateSwapBuffers(
P3_THUNKEDDATA* pThisDisplay,
LPD3DHAL_DRAWPRIMITIVES2DATA pdp2d ,
P3_VERTEXBUFFERINFO* pVertexBufferInfo,
P3_VERTEXBUFFERINFO* pCommandBufferInfo)
{
// Add the buffers to the pending queue.
// Only do this if the buffers actually belong to us.
// If either of the buffers was sent, update the HOSTIN ID.
// We need to make the new sequence ID and the update of the hostin
// 'atomic', or the wraparound will cause a lockup
if (pVertexBufferInfo)
{
P3_DMA_DEFS();
pVertexBufferInfo->dwSequenceID =
__EB_GetNewSequenceID(pThisDisplay);
__EB_AddToBufferQueue(pThisDisplay, pVertexBufferInfo);
P3_DMA_GET_BUFFER_ENTRIES( 2 );
SEND_P3_DATA(HostInID, pVertexBufferInfo->dwSequenceID);
P3_DMA_COMMIT_BUFFER();
}
if (pCommandBufferInfo)
{
P3_DMA_DEFS();
pCommandBufferInfo->dwSequenceID =
__EB_GetNewSequenceID(pThisDisplay);
__EB_AddToBufferQueue(pThisDisplay, pCommandBufferInfo);
P3_DMA_GET_BUFFER_ENTRIES( 2 );
SEND_P3_DATA(HostInID, pCommandBufferInfo->dwSequenceID);
P3_DMA_COMMIT_BUFFER();
}
if (D3DHALDP2_SWAPVERTEXBUFFER & pdp2d->dwFlags)
{
DWORD dwNewSize = pdp2d->lpDDVertex->lpGbl->dwLinearSize;
DISPDBG((DBGLVL,"D3DHALDP2_SWAPVERTEXBUFFER..."));
if (D3DHALDP2_REQVERTEXBUFSIZE & pdp2d->dwFlags)
{
DISPDBG((DBGLVL,"D3DHALDP2_REQVERTEXBUFSIZE - %d",
pdp2d->dwReqVertexBufSize));
if (dwNewSize < pdp2d->dwReqVertexBufSize)
{
dwNewSize = pdp2d->dwReqVertexBufSize;
}
}
DISPDBG((DBGLVL,"Current vertex buffer size: %d, "
"New size will be: %d",
pdp2d->lpDDVertex->lpGbl->dwLinearSize,
dwNewSize));
// The vertex buffer we just sent off is fixed in place until we
// mark it as not in use, which we will after allocating a new
// buffer. The following call will try to get a new buffer and
// update the surface structure appropriately. Note that it won't
// trash the current surface unless the allocation succeeds.
if (__EB_AllocateCachedBuffer(pThisDisplay,
dwNewSize,
pdp2d->lpDDVertex))
{
DISPDBG((DBGLVL,"Got a new swap vertex buffer"));
#define STAMP_BUFFER 0
#if STAMP_BUFFER
{
DWORD i, *pv;
pv = (DWORD * ) pdp2d->lpDDVertex->lpGbl->fpVidMem;
for( i = 0; i < ( dwNewSize / 4 ); i++ )
{
*pv++ = 0x44000000;
}
}
#endif
// Fix up the discarded buffer if required
if (pVertexBufferInfo)
{
// Mark the current buffer as not in use, meaning it can
// be freed once it has cleared P3. This might occur the
// next time we are here.
pVertexBufferInfo->bInUse = FALSE;
// A gotcha! The buffer we just launched was consumed so
// fast that it was freed from the pending list to make
// room for it's replacement. This is normally OK, but in
// this case the buffer we freed isn't being put back
// anywhere - i.e. no surface now owns it, and the memory
// associated with it wasn't freed because as far as the
// driver is concerned it is still in use until it is
// replaced due to the above succesfull call. The
// 'solution' is to add it back into the queue if it is
// not in it, and make sure that it is marked as not in
// use and at a 0 hostin ID.
if (!pVertexBufferInfo->pPrev)
{
pVertexBufferInfo->dwSequenceID = 0;
__EB_AddToBufferQueue(pThisDisplay,
pVertexBufferInfo);
}
}
}
else
{
// Couldn't swap this buffer, so we have to wait
DISPDBG((DBGLVL,"Not swapping vertex buffer "
"due to lack of space!"));
__EB_Wait(pThisDisplay, pVertexBufferInfo);
}
}
else
{
DISPDBG((DBGLVL,"No vertex buffer swapping..."));
}
if (D3DHALDP2_SWAPCOMMANDBUFFER & pdp2d->dwFlags)
{
DWORD dwNewSize = pdp2d->lpDDCommands->lpGbl->dwLinearSize;
DISPDBG((DBGLVL,"D3DHALDP2_SWAPCOMMANDBUFFER..."));
if (D3DHALDP2_REQCOMMANDBUFSIZE & pdp2d->dwFlags)
{
DISPDBG((DBGLVL,"D3DHALDP2_REQCOMMANDBUFSIZE - %d",
pdp2d->dwReqCommandBufSize));
if (dwNewSize < pdp2d->dwReqCommandBufSize)
{
dwNewSize = pdp2d->dwReqCommandBufSize;
}
}
DISPDBG((DBGLVL,"Current command buffer size: "
"%d, New size will be: %d",
pdp2d->lpDDCommands->lpGbl->dwLinearSize,
dwNewSize));
// The command buffer we just sent off is fixed in place until we
// mark it as not in use, which we will after allocating a new
// buffer. The following call will try to get a new buffer and
// update the surface structure appropriately. Note that it won't
// trash the current surface unless the allocation succeeds
if (__EB_AllocateCachedBuffer(pThisDisplay,
dwNewSize,
pdp2d->lpDDCommands))
{
DISPDBG((DBGLVL,"Got a new swap command buffer"));
// Fix up the previous command buffer if required.
if (pCommandBufferInfo)
{
// Mark the current buffer as not in use, meaning it can
// be freed once it has cleared P3. This might occur the
// next time we are here.
pCommandBufferInfo->bInUse = FALSE;
// A gotcha! The buffer we just launched was consumed so
// fast that it was freed from the pending list to make
// room for it's replacement. This is normally OK, but in
// this case the buffer we freed isn't being put back
// anywhere - i.e. no surface now owns it, and the memory
// associated with it wasn't freed because as far as the
// driver is concerned it is still in use until it is
// replaced due to the above succesfull call. The
// 'solution' is to add it back into the queue if it is
// not in it, and make sure that it is marked as not in
// use and at a 0 hostin ID.
if (!pCommandBufferInfo->pPrev)
{
pCommandBufferInfo->dwSequenceID = 0;
__EB_AddToBufferQueue(pThisDisplay,
pCommandBufferInfo);
}
}
}
else
{
// Couldn't swap this buffer, so we have to wait
DISPDBG((DBGLVL,"Not swapping command buffer "
"due to lack of space!"));
__EB_Wait(pThisDisplay, pCommandBufferInfo);
}
}
else
{
DISPDBG((DBGLVL,"No Command buffer swapping..."));
}
} // _D3D_EB_UpdateSwapBuffers
//-----------------------------Public Routine----------------------------------
//
// D3DCanCreateD3DBuffer
//
// Called by the runtime to ask if a type of vertex/command buffer can
// be created by the driver. We don't do anything here at present
//
//
//-----------------------------------------------------------------------------
DWORD CALLBACK
D3DCanCreateD3DBuffer(
LPDDHAL_CANCREATESURFACEDATA pccsd)
{
P3_THUNKEDDATA* pThisDisplay;
DBG_CB_ENTRY(D3DCanCreateD3DBuffer);
GET_THUNKEDDATA(pThisDisplay, pccsd->lpDD);
VALIDATE_MODE_AND_STATE(pThisDisplay);
DBGDUMP_DDSURFACEDESC(DBGLVL, pccsd->lpDDSurfaceDesc);
pccsd->ddRVal = DD_OK;
DBG_CB_EXIT(D3DCanCreateD3DBuffer,pccsd->ddRVal);
return DDHAL_DRIVER_HANDLED;
} // D3DCanCreateD3DBuffer
//-----------------------------Public Routine----------------------------------
//
// D3DCreateD3DBuffer
//
// Called by the runtime to create a vertex buffer. We try to allocate
// from our cached heap here.
//
//
//-----------------------------------------------------------------------------
DWORD CALLBACK
D3DCreateD3DBuffer(
LPDDHAL_CREATESURFACEDATA pcsd)
{
P3_THUNKEDDATA* pThisDisplay;
LPDDRAWI_DDRAWSURFACE_LCL pSurf;
LPDDRAWI_DDRAWSURFACE_LCL FAR* ppSList;
BOOL bHandled = FALSE;
DWORD i;
DBG_CB_ENTRY(D3DCreateD3DBuffer);
GET_THUNKEDDATA(pThisDisplay, pcsd->lpDD);
VALIDATE_MODE_AND_STATE(pThisDisplay);
STOP_SOFTWARE_CURSOR(pThisDisplay);
DDRAW_OPERATION(pContext, pThisDisplay);
ppSList = pcsd->lplpSList;
for (i = 0; i < pcsd->dwSCnt; i++)
{
pSurf = ppSList[i];
// Allocate the size we want
DISPDBG((DBGLVL,"Surface %d requested is 0x%x big",
i, pSurf->lpGbl->dwLinearSize));
DBGDUMP_DDRAWSURFACE_LCL(DBGLVL, pSurf);
pSurf->lpGbl->dwReserved1 = 0;
// A 32 kB command buffer gives a high probability of being allowed
// to swap the associated vertex buffer
if( pSurf->lpGbl->dwLinearSize < _32_KBYTES )
{
pSurf->lpGbl->dwLinearSize = _32_KBYTES;
}
if (__EB_AllocateCachedBuffer(pThisDisplay,
pSurf->lpGbl->dwLinearSize,
pSurf))
{
DISPDBG((DBGLVL,"Allocated a new cached buffer for use by D3D"));
bHandled = TRUE;
}
else
{
// If we can't find a buffer, the best thing to do is to
// punt to D3D and always copy the data into a DMA buffer
// (because it won't be contiguous). The DP2 call should
// check the reserved field before using the HostIn unit
DISPDBG((ERRLVL,"WARNING: Couldn't find any vertex memory"
" in the heap or in the sent list!"));
pSurf->lpGbl->dwReserved1 = 0;
bHandled = FALSE;
}
}
START_SOFTWARE_CURSOR(pThisDisplay);
pcsd->ddRVal = DD_OK;
if (bHandled)
{
DBG_EXIT(D3DCreateD3DBuffer,DDHAL_DRIVER_HANDLED);
return DDHAL_DRIVER_HANDLED;
}
else
{
DBG_CB_EXIT(D3DCreateD3DBuffer,DDHAL_DRIVER_NOTHANDLED);
return DDHAL_DRIVER_NOTHANDLED;
}
} // D3DCreateD3DBuffer
//-----------------------------Public Routine----------------------------------
//
// D3DDestroyD3DBuffer
//
// Called by the runtime to destroy a vertex buffer. We free the buffer
// from our memory heap and the current queue.
//
//
//-----------------------------------------------------------------------------
DWORD CALLBACK
D3DDestroyD3DBuffer(
LPDDHAL_DESTROYSURFACEDATA pdd)
{
P3_THUNKEDDATA* pThisDisplay;
DBG_CB_ENTRY(D3DDestroyD3DBuffer);
GET_THUNKEDDATA(pThisDisplay, pdd->lpDD);
VALIDATE_MODE_AND_STATE(pThisDisplay);
STOP_SOFTWARE_CURSOR(pThisDisplay);
DDRAW_OPERATION(pContext, pThisDisplay);
// Debug data
DBGDUMP_DDRAWSURFACE_LCL(DBGLVL, pdd->lpDDSurface);
// Free the D3D buffer. If its in use, we will wait for it to be ready.
__EB_FreeCachedBuffer(pThisDisplay, pdd->lpDDSurface);
#ifdef CHECK_BUFFERS_ARENT_LEFT_AFTER_APPLICATION_SHUTDOWN
// Flush all the buffers
// This checks that the queue is OK. If you don't do this
// you may see the linear allocator on the 16 bit side complain
// that there is freeable memory there. This is quite alright.
_D3D_EB_FlushAllBuffers(pThisDisplay , TRUE);
#endif
START_SOFTWARE_CURSOR(pThisDisplay);
// We don't handle the call because DDRAW has allocated out of AGP memory
pdd->ddRVal = DD_OK;
DBG_CB_EXIT(D3DDestroyD3DBuffer,DDHAL_DRIVER_HANDLED);
return DDHAL_DRIVER_HANDLED;
} // D3DDestroyD3DBuffer
//-----------------------------Public Routine----------------------------------
//
// D3DLockD3DBuffer
//
// Called by the runtime to lock a vertex buffer. We make sure
// it has been consumed by the queue, then we continue.
//
//
//-----------------------------------------------------------------------------
DWORD CALLBACK
D3DLockD3DBuffer(
LPDDHAL_LOCKDATA pld)
{
P3_THUNKEDDATA* pThisDisplay;
P3_VERTEXBUFFERINFO* pCurrentBuffer;
DBG_CB_ENTRY(D3DLockD3DBuffer);
GET_THUNKEDDATA(pThisDisplay, pld->lpDD);
VALIDATE_MODE_AND_STATE(pThisDisplay);
DBGDUMP_DDRAWSURFACE_LCL(DBGLVL, pld->lpDDSurface);
STOP_SOFTWARE_CURSOR(pThisDisplay);
DDRAW_OPERATION(pContext, pThisDisplay);
if (pld->bHasRect)
{
DISPDBG((ERRLVL,"Trying to lock a rect in a D3D buffer (error):"));
DISPDBG((ERRLVL,"left:%d, top:%d, right:%d, bottom:%d",
pld->rArea.left, pld->rArea.top,
pld->rArea.right, pld->rArea.bottom));
// This is just a debugging aid
// We will ignore any rects requested and lock the whole buffer
}
// If the buffer has a next pointer then it is in the circular list
// and we need to wait for the chip to finish consuming it.
pCurrentBuffer = (P3_VERTEXBUFFERINFO*)pld->lpDDSurface->lpGbl->dwReserved1;
if (pCurrentBuffer)
{
// Wait for the buffer to be consumed
__EB_Wait(pThisDisplay, pCurrentBuffer);
// Remove it from the queue
if (__EB_RemoveFromBufferQueue(pThisDisplay, pCurrentBuffer))
{
// There was an error removing it from the queue
DISPDBG((ERRLVL,"ERROR: This buffer should not have been freed"
"(its in use!)"));
}
}
else
{
DISPDBG((WRNLVL,"Buffer was not allocated by the driver"));
}
START_SOFTWARE_CURSOR(pThisDisplay);
// Return the pointer
pld->lpSurfData = (LPVOID)pld->lpDDSurface->lpGbl->fpVidMem;
DISPDBG((DBGLVL,"Returning 0x%x for locked buffer address",
pld->lpDDSurface->lpGbl->fpVidMem));
pld->ddRVal = DD_OK;
DBG_CB_EXIT(D3DLockD3DBuffer,DDHAL_DRIVER_HANDLED);
return DDHAL_DRIVER_HANDLED;
} // D3DLockD3DBuffer
//-----------------------------Public Routine----------------------------------
//
// D3DUnlockD3DBuffer
//
// Called by the runtime to unlock a vertex buffer.
//
//-----------------------------------------------------------------------------
DWORD CALLBACK
D3DUnlockD3DBuffer(
LPDDHAL_UNLOCKDATA puld)
{
DBG_CB_ENTRY(D3DUnlockD3DBuffer);
// We don't need to do anything special here.
puld->ddRVal = DD_OK;
DBG_CB_EXIT(D3DUnlockD3DBuffer,DDHAL_DRIVER_HANDLED);
return DDHAL_DRIVER_HANDLED;
} // D3DUnlockD3DBuffer
#endif // DX7_VERTEXBUFFERS
//@@END_DDKSPLIT