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