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.
2049 lines
78 KiB
2049 lines
78 KiB
/*==========================================================================;
|
|
*
|
|
* Copyright (C) 1997 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: cbhal.cpp
|
|
* Content: DrawPrimitive implementation for command buffer HALs
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "pch.cpp"
|
|
#pragma hdrstop
|
|
#include "drawprim.hpp"
|
|
#include "clipfunc.h"
|
|
#include "d3dfei.h"
|
|
#include "pvvid.h"
|
|
#if DBG
|
|
// #define VALIDATE_DP2CMD
|
|
#endif
|
|
|
|
extern "C" HRESULT WINAPI
|
|
DDInternalLock( LPDDRAWI_DDRAWSURFACE_LCL this_lcl, LPVOID* lpBits );
|
|
extern "C" FLATPTR GetAliasedVidMem( LPDDRAWI_DIRECTDRAW_LCL pdrv_lcl,
|
|
LPDDRAWI_DDRAWSURFACE_LCL surf_lcl,
|
|
FLATPTR fpVidMem );
|
|
|
|
#ifndef WIN95
|
|
extern BOOL bVBSwapEnabled, bVBSwapWorkaround;
|
|
#endif // WIN95
|
|
|
|
// Command buffer size tuned to 16K to minimize flushes in Unreal
|
|
const DWORD CDirect3DDeviceIDP2::dwD3DDefaultCommandBatchSize = 16384; // * 1 = 16K bytes
|
|
|
|
inline void CDirect3DDeviceIDP2::ClearBatch(bool bWithinPrimitive)
|
|
{
|
|
// Reset command buffer
|
|
lpDP2CurrCommand = (LPD3DHAL_DP2COMMAND)lpvDP2Commands;
|
|
dwDP2CommandLength = 0;
|
|
dp2data.dwCommandOffset = 0;
|
|
dp2data.dwCommandLength = 0;
|
|
bDP2CurrCmdOP = 0;
|
|
// Reset vertex buffer
|
|
if (!bWithinPrimitive)
|
|
{
|
|
dp2data.dwVertexOffset = 0;
|
|
this->dwDP2VertexCount = 0;
|
|
dwVertexBase = 0;
|
|
TLVbuf_Base() = 0;
|
|
if (dp2data.dwFlags & D3DHALDP2_USERMEMVERTICES)
|
|
{
|
|
// We are flushing a user mem primitive.
|
|
// We need to clear dp2data.lpUMVertices
|
|
// since we are done with it. We replace
|
|
// it with TLVbuf.
|
|
DDASSERT(lpDP2CurrBatchVBI == NULL);
|
|
dp2data.lpDDVertex = ((LPDDRAWI_DDRAWSURFACE_INT)TLVbuf_GetDDS())->lpLcl;
|
|
lpDP2CurrBatchVBI = TLVbuf_GetVBI();
|
|
lpDP2CurrBatchVBI->AddRef();
|
|
dp2data.dwFlags &= ~D3DHALDP2_USERMEMVERTICES;
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::CheckSurfaces()"
|
|
HRESULT CDirect3DDeviceIDP2::CheckSurfaces()
|
|
{
|
|
HRESULT hr;
|
|
if(this->lpDirect3DI->lpTextureManager->CheckIfLost())
|
|
{
|
|
D3D_ERR("Managed Textures lost");
|
|
return DDERR_SURFACELOST;
|
|
}
|
|
if ( ((LPDDRAWI_DDRAWSURFACE_INT) this->lpDDSTarget)->lpLcl->lpGbl->dwUsageCount ||
|
|
(this->lpDDSZBuffer && ((LPDDRAWI_DDRAWSURFACE_INT) this->lpDDSZBuffer)->lpLcl->lpGbl->dwUsageCount) )
|
|
{
|
|
D3D_ERR("Render target or Z buffer locked");
|
|
return DDERR_SURFACEBUSY;
|
|
}
|
|
if ( ((LPDDRAWI_DDRAWSURFACE_INT) this->lpDDSTarget)->lpLcl->dwFlags & DDRAWISURF_INVALID )\
|
|
{
|
|
D3D_ERR("Render target buffer lost");
|
|
return DDERR_SURFACELOST;
|
|
}
|
|
if ( this->lpDDSZBuffer && ( ((LPDDRAWI_DDRAWSURFACE_INT) this->lpDDSZBuffer)->lpLcl->dwFlags & DDRAWISURF_INVALID ) )
|
|
{
|
|
D3D_ERR("Z buffer lost");
|
|
return DDERR_SURFACELOST;
|
|
}
|
|
if (!(this->dp2data.dwFlags & D3DHALDP2_USERMEMVERTICES) && (this->dp2data.lpDDVertex) && (this->dp2data.lpDDVertex->dwFlags & DDRAWISURF_INVALID))
|
|
{
|
|
D3D_ERR("Vertex buffer lost");
|
|
return DDERR_SURFACELOST;
|
|
}
|
|
if (this->TLVbuf_GetDDS())
|
|
{
|
|
LPDDRAWI_DDRAWSURFACE_LCL lpLcl = ((LPDDRAWI_DDRAWSURFACE_INT)(this->TLVbuf_GetDDS()))->lpLcl;
|
|
if (lpLcl->dwFlags & DDRAWISURF_INVALID)
|
|
{
|
|
D3D_ERR("Internal vertex buffer lost");
|
|
return DDERR_SURFACELOST;
|
|
}
|
|
}
|
|
if (this->dp2data.lpDDCommands->dwFlags & DDRAWISURF_INVALID)
|
|
{
|
|
D3D_ERR("Command buffer lost");
|
|
return DDERR_SURFACELOST;
|
|
}
|
|
return D3D_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::FlushStates(void)"
|
|
|
|
HRESULT CDirect3DDeviceIDP2::FlushStates(bool bWithinPrimitive)
|
|
{
|
|
HRESULT dwRet=D3D_OK;
|
|
if (dwFlags & D3DPV_WITHINPRIMITIVE)
|
|
bWithinPrimitive = true;
|
|
if (dwDP2CommandLength) // Do we have some instructions to flush ?
|
|
{
|
|
CLockD3DST lockObject(this, DPF_MODNAME, REMIND("")); // Takes D3D lock (ST only).
|
|
++m_qwBatch;
|
|
// So that currently bound textures get rebatched
|
|
for (DWORD dwStage = 0; dwStage < this->dwMaxTextureBlendStages; dwStage++)
|
|
{
|
|
LPDIRECT3DTEXTUREI lpTexI = this->lpD3DMappedTexI[dwStage];
|
|
if (NULL != lpTexI)
|
|
{
|
|
if(lpTexI->lpDDS != NULL)
|
|
{
|
|
BatchTexture(((LPDDRAWI_DDRAWSURFACE_INT)(lpTexI->lpDDS))->lpLcl);
|
|
}
|
|
}
|
|
}
|
|
// Check if render target and / or z buffer is lost
|
|
if ((dwRet = CheckSurfaces()) != D3D_OK)
|
|
{ // If lost, we'll just chuck all this work into the bit bucket
|
|
ClearBatch(bWithinPrimitive);
|
|
if (dwRet == DDERR_SURFACELOST)
|
|
{
|
|
this->dwFEFlags |= D3DFE_LOSTSURFACES;
|
|
dwRet = D3D_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Save since it will get overwritten by ddrval after DDI call
|
|
DWORD dwVertexSize = dp2data.dwVertexSize;
|
|
dp2data.dwVertexLength = this->dwDP2VertexCount;
|
|
dp2data.dwCommandLength = dwDP2CommandLength;
|
|
//we clear this to break re-entering as SW rasterizer needs to lock DDRAWSURFACE
|
|
dwDP2CommandLength = 0;
|
|
// Try and set these 2 values only once during initialization
|
|
dp2data.dwhContext = this->dwhContext;
|
|
dp2data.lpdwRStates = this->rstates;
|
|
DDASSERT(dp2data.dwVertexSize != 0);
|
|
D3D_INFO(6, "dwVertexType passed to the driver = 0x%08x", dp2data.dwVertexType);
|
|
|
|
// If we need the same TLVbuf next time do not swap buffers.
|
|
// Save and restore this bit
|
|
bool bSwapVB = (dp2data.dwFlags & D3DHALDP2_SWAPVERTEXBUFFER) != 0;
|
|
if (bWithinPrimitive)
|
|
{
|
|
dp2data.dwFlags &= ~D3DHALDP2_SWAPVERTEXBUFFER;
|
|
}
|
|
// At the end of the DP2 call we expect the VB to be unlocked if
|
|
// 1. We cannot allow the driver to swap the VB
|
|
// 2. We are using a VB (not USERMEMVERTICES)
|
|
// 3. It is not TLVbuf
|
|
// In this case we might as well tell the driver that it is unlocked.
|
|
// More importantly, we need to let DDraw know that the VB is unlocked.
|
|
if (!(dp2data.dwFlags & D3DHALDP2_SWAPVERTEXBUFFER))
|
|
{
|
|
if ((lpDP2CurrBatchVBI) && (lpDP2CurrBatchVBI != TLVbuf_GetVBI()))
|
|
{
|
|
lpDP2CurrBatchVBI->UnlockI();
|
|
}
|
|
}
|
|
#ifndef WIN95
|
|
else if (bVBSwapWorkaround && lpDP2CurrBatchVBI != 0 && lpDP2CurrBatchVBI == TLVbuf_GetVBI())
|
|
{
|
|
lpDP2CurrBatchVBI->UnlockWorkAround();
|
|
}
|
|
if (!bVBSwapEnabled) // Note: bVBSwapEnabled not the same as bSwapVB above.
|
|
// bVBSwapEnabled is a global to indicate whether VB
|
|
// VB swapping should be turned off due to broken
|
|
// Win2K kernel implementation
|
|
{
|
|
dp2data.dwFlags &= ~D3DHALDP2_SWAPVERTEXBUFFER;
|
|
}
|
|
if (!dp2data.lpDDCommands->hDDSurface)
|
|
CompleteCreateSysmemSurface(dp2data.lpDDCommands);
|
|
if (!(dp2data.dwFlags & D3DHALDP2_USERMEMVERTICES )
|
|
&& !dp2data.lpDDVertex->hDDSurface)
|
|
CompleteCreateSysmemSurface(dp2data.lpDDVertex);
|
|
#else
|
|
// Take Win 16 Lock here
|
|
LOCK_HAL( dwRet, this );
|
|
#endif //WIN95
|
|
|
|
// Spin waiting on the driver if wait requested
|
|
do {
|
|
// Need to set this since the driver may have overwrote it by
|
|
// setting ddrval = DDERR_WASSTILLDRAWING
|
|
dp2data.dwVertexSize = dwVertexSize;
|
|
CALL_HAL3ONLY_NOLOCK(dwRet, this, DrawPrimitives2, &dp2data);
|
|
if (dwRet != DDHAL_DRIVER_HANDLED)
|
|
{
|
|
D3D_ERR ( "Driver not handled in DrawPrimitives2" );
|
|
// Need sensible return value in this case,
|
|
// currently we return whatever the driver stuck in here.
|
|
}
|
|
} while (dp2data.ddrval == DDERR_WASSTILLDRAWING);
|
|
if (dp2data.ddrval == D3DERR_COMMAND_UNPARSED)
|
|
{ // This should never occur since the driver must understand
|
|
// all the instruction we batch.
|
|
D3D_ERR("Driver could not parse this batch!");
|
|
dwRet = DDERR_GENERIC; // Some thing better here ?
|
|
}
|
|
else
|
|
{
|
|
dwRet= dp2data.ddrval;
|
|
// update command buffer pointer
|
|
if ((dwRet == D3D_OK) && (dp2data.dwFlags & D3DHALDP2_SWAPCOMMANDBUFFER))
|
|
{
|
|
#ifdef WIN95
|
|
// Get Aliased vid mem pointer if it is a vid mem surf.
|
|
if (dp2data.dwFlags & D3DHALDP2_VIDMEMCOMMANDBUF)
|
|
{
|
|
D3D_INFO(7, "Got back new vid mem command buffer");
|
|
FLATPTR paliasbits = GetAliasedVidMem( dp2data.lpDDCommands->lpSurfMore->lpDD_lcl,
|
|
dp2data.lpDDCommands, (FLATPTR) dp2data.lpDDCommands->lpGbl->fpVidMem );
|
|
if (paliasbits == NULL)
|
|
{
|
|
DPF_ERR("Could not get Aliased pointer for vid mem command buffer");
|
|
// Since we can't use this pointer, set it's size to 0
|
|
// That way next time around we will try and allocate a new one
|
|
dp2data.lpDDCommands->lpGbl->dwLinearSize = 0;
|
|
}
|
|
lpvDP2Commands = (LPVOID)paliasbits;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
D3D_INFO(7, "Got back new sys mem command buffer");
|
|
lpvDP2Commands = (LPVOID)dp2data.lpDDCommands->lpGbl->fpVidMem;
|
|
}
|
|
dwDP2CommandBufSize = dp2data.lpDDCommands->lpGbl->dwLinearSize;
|
|
}
|
|
// update vertex buffer pointer
|
|
if ((dwRet == D3D_OK) && (dp2data.dwFlags & D3DHALDP2_SWAPVERTEXBUFFER) && dp2data.lpDDVertex)
|
|
{
|
|
FLATPTR paliasbits;
|
|
#ifdef WIN95
|
|
if (dp2data.dwFlags & D3DHALDP2_VIDMEMVERTEXBUF)
|
|
{
|
|
paliasbits = GetAliasedVidMem( dp2data.lpDDVertex->lpSurfMore->lpDD_lcl,
|
|
dp2data.lpDDVertex, (FLATPTR) dp2data.lpDDVertex->lpGbl->fpVidMem );
|
|
if (paliasbits == NULL)
|
|
{
|
|
DPF_ERR("Could not get Aliased pointer for vid mem vertex buffer");
|
|
// Since we can't use this pointer, set it's size to 0
|
|
// That way next time around we will try and allocate a new one
|
|
dp2data.lpDDVertex->lpGbl->dwLinearSize = 0;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
paliasbits = dp2data.lpDDVertex->lpGbl->fpVidMem;
|
|
}
|
|
if (lpDP2CurrBatchVBI == TLVbuf_GetVBI())
|
|
{
|
|
#if DBG
|
|
if(this->alignedBuf != (VOID*)paliasbits)
|
|
{
|
|
D3D_INFO(2, "Driver swapped TLVBuf pointer in FlushStates");
|
|
}
|
|
#endif //DBG
|
|
this->alignedBuf = (LPVOID)paliasbits;
|
|
this->TLVbuf_size = dp2data.lpDDVertex->lpGbl->dwLinearSize;
|
|
}
|
|
else
|
|
{
|
|
#if DBG
|
|
if(this->lpDP2CurrBatchVBI->position.lpvData != (VOID*)paliasbits)
|
|
{
|
|
D3D_INFO(2, "Driver swapped VB pointer in FlushStates");
|
|
}
|
|
#endif //DBG
|
|
this->lpDP2CurrBatchVBI->position.lpvData = (LPVOID)paliasbits;
|
|
}
|
|
}
|
|
}
|
|
#ifdef WIN95
|
|
// Release Win16 Lock here
|
|
UNLOCK_HAL( this );
|
|
#else
|
|
if (!bWithinPrimitive && bSwapVB && bVBSwapWorkaround && lpDP2CurrBatchVBI != 0 && lpDP2CurrBatchVBI == TLVbuf_GetVBI())
|
|
{
|
|
HRESULT hr = lpDP2CurrBatchVBI->LockWorkAround(this);
|
|
if (FAILED(hr))
|
|
{
|
|
TLVbuf_base = 0;
|
|
TLVbuf_size = 0;
|
|
D3D_ERR("Driver failed Lock in FlushStates");
|
|
if (SUCCEEDED(dwRet))
|
|
{
|
|
dwRet = hr;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
// Restore flag if necessary
|
|
if (bSwapVB)
|
|
dp2data.dwFlags |= D3DHALDP2_SWAPVERTEXBUFFER;
|
|
// Restore to value before the DDI call
|
|
dp2data.dwVertexSize = dwVertexSize;
|
|
ClearBatch(bWithinPrimitive);
|
|
}
|
|
}
|
|
// There are situations when the command stream has no data, but there is data in
|
|
// the vertex pool. This could happen, for instance if every triangle got rejected
|
|
// while clipping. In this case we still need to "Flush out" the vertex data.
|
|
else if (dp2data.dwCommandLength == 0)
|
|
{
|
|
ClearBatch(bWithinPrimitive);
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::FlushStates(DWORD)"
|
|
|
|
HRESULT CDirect3DDeviceIDP2::FlushStatesReq(DWORD dwReqSize)
|
|
{
|
|
DWORD sav = (dp2data.dwFlags & D3DHALDP2_SWAPVERTEXBUFFER);
|
|
dp2data.dwReqVertexBufSize = dwReqSize;
|
|
dp2data.dwFlags |= D3DHALDP2_SWAPVERTEXBUFFER | D3DHALDP2_REQVERTEXBUFSIZE;
|
|
HRESULT ret = FlushStates();
|
|
dp2data.dwFlags &= ~(D3DHALDP2_SWAPVERTEXBUFFER | D3DHALDP2_REQVERTEXBUFSIZE);
|
|
dp2data.dwFlags |= sav;
|
|
return ret;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::GrowCommandBuffer"
|
|
// Check and grow command buffer
|
|
HRESULT CDirect3DDeviceIDP2::GrowCommandBuffer(LPDIRECT3DI lpD3DI, DWORD dwSize)
|
|
{
|
|
HRESULT ret;
|
|
if (dwSize > dwDP2CommandBufSize)
|
|
{
|
|
if (lpDDSCB1)
|
|
{
|
|
lpDDSCB1->Release();
|
|
lpDDSCB1 = NULL;
|
|
}
|
|
// Create command buffer through DirectDraw
|
|
DDSURFACEDESC2 ddsd;
|
|
memset(&ddsd, 0, sizeof(DDSURFACEDESC2));
|
|
ddsd.dwSize = sizeof(DDSURFACEDESC2);
|
|
ddsd.dwFlags = DDSD_WIDTH | DDSD_CAPS;
|
|
ddsd.dwWidth = dwSize;
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_EXECUTEBUFFER;
|
|
if (IS_HW_DEVICE(this))
|
|
ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
|
|
else
|
|
ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
|
|
ddsd.ddsCaps.dwCaps2 = DDSCAPS2_COMMANDBUFFER;
|
|
// Try explicit video memory first
|
|
D3D_INFO(7, "Trying to create a vid mem command buffer");
|
|
ret = lpD3DI->lpDD7->CreateSurface(&ddsd, &lpDDSCB1, NULL);
|
|
if (ret != DD_OK)
|
|
{
|
|
// If that failed, try explicit system memory
|
|
ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY;
|
|
ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
|
|
D3D_INFO(7, "Trying to create a sys mem command buffer");
|
|
ret = lpD3DI->lpDD7->CreateSurface(&ddsd, &lpDDSCB1, NULL);
|
|
if (ret != DD_OK)
|
|
{
|
|
D3D_ERR("failed to allocate Command Buffer 1");
|
|
dwDP2CommandBufSize = 0;
|
|
return ret;
|
|
}
|
|
}
|
|
// Lock command buffer
|
|
ret = lpDDSCB1->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
|
|
if (ret != DD_OK)
|
|
{
|
|
D3D_ERR("Could not lock command buffer.");
|
|
lpDDSCB1->Release();
|
|
lpDDSCB1 = NULL;
|
|
dwDP2CommandBufSize = 0;
|
|
return ret;
|
|
}
|
|
// update command buffer pointer
|
|
lpvDP2Commands = ddsd.lpSurface;
|
|
dp2data.lpDDCommands = ((LPDDRAWI_DDRAWSURFACE_INT)lpDDSCB1)->lpLcl;
|
|
dwDP2CommandBufSize = dwSize;
|
|
}
|
|
return D3D_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::Init"
|
|
|
|
HRESULT CDirect3DDeviceIDP2::Init(REFCLSID riid, LPDIRECT3DI lpD3DI, LPDIRECTDRAWSURFACE lpDDS,
|
|
IUnknown* pUnkOuter, LPUNKNOWN* lplpD3DDevice)
|
|
{
|
|
dwDP2CommandBufSize = 0;
|
|
dwDP2Flags =0;
|
|
lpDDSCB1 = NULL;
|
|
lpvDP2Commands = NULL;
|
|
TLVbuf_size = 0;
|
|
allocatedBuf = 0;
|
|
alignedBuf = 0;
|
|
TLVbuf_base = 0;
|
|
dwTLVbufChanges = 0;
|
|
pNullVB = NULL;
|
|
// We do this early in case of DP2 since GrowCommandBuffer depends on this check
|
|
if (IsEqualIID(riid, IID_IDirect3DHALDevice) || IsEqualIID(riid, IID_IDirect3DTnLHalDevice))
|
|
{
|
|
this->dwFEFlags |= D3DFE_REALHAL;
|
|
}
|
|
HRESULT ret = GrowCommandBuffer(lpD3DI, dwD3DDefaultCommandBatchSize);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
// Fill the dp2data structure with initial values
|
|
dp2data.dwFlags = D3DHALDP2_SWAPCOMMANDBUFFER;
|
|
dp2data.dwVertexType = D3DFVF_TLVERTEX; // Initial assumption
|
|
dp2data.dwVertexSize = sizeof(D3DTLVERTEX); // Initial assumption
|
|
ClearBatch(false);
|
|
|
|
// Initialize the DDI independent part of the device
|
|
ret = DIRECT3DDEVICEI::Init(riid, lpD3DI, lpDDS, pUnkOuter, lplpD3DDevice);
|
|
if (ret != D3D_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
// Since we plan to call TLV_Grow for the first time with "true"
|
|
this->dwDeviceFlags |= D3DDEV_TLVBUFWRITEONLY;
|
|
if (TLVbuf_Grow((__INIT_VERTEX_NUMBER*2)*sizeof(D3DTLVERTEX), true) != DD_OK)
|
|
{
|
|
D3D_ERR( "Out of memory in DeviceCreate (TLVbuf)" );
|
|
return DDERR_OUTOFMEMORY;
|
|
}
|
|
D3DVERTEXBUFFERDESC vbdesc;
|
|
vbdesc.dwSize = sizeof(D3DVERTEXBUFFERDESC);
|
|
vbdesc.dwCaps = D3DVBCAPS_SYSTEMMEMORY;
|
|
vbdesc.dwFVF = D3DFVF_TLVERTEX;
|
|
vbdesc.dwNumVertices = 1;
|
|
ret = this->lpDirect3DI->CreateVertexBufferI(&vbdesc, &this->pNullVB, 0);
|
|
if (ret != DD_OK)
|
|
{
|
|
return ret;
|
|
}
|
|
#ifdef VTABLE_HACK
|
|
if (!IS_MT_DEVICE(this))
|
|
{
|
|
// Make SetRS point to execute mode
|
|
VtblSetRenderStateExecute();
|
|
VtblSetTextureStageStateExecute();
|
|
VtblSetTextureExecute();
|
|
VtblApplyStateBlockExecute();
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::~CDirect3DDeviceIDP2"
|
|
|
|
CDirect3DDeviceIDP2::~CDirect3DDeviceIDP2()
|
|
{
|
|
CleanupTextures();
|
|
if (pNullVB)
|
|
pNullVB->Release();
|
|
if (allocatedBuf)
|
|
allocatedBuf->Release();
|
|
if (lpDDSCB1)
|
|
lpDDSCB1->Release();
|
|
if (lpDP2CurrBatchVBI)
|
|
{
|
|
lpDP2CurrBatchVBI->lpDevIBatched = NULL;
|
|
lpDP2CurrBatchVBI->Release();
|
|
}
|
|
}
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::SetRenderStateI"
|
|
|
|
HRESULT CDirect3DDeviceIDP2::SetRenderStateI(D3DRENDERSTATETYPE dwStateType,
|
|
DWORD value)
|
|
{
|
|
HRESULT ret = D3D_OK;
|
|
if (bDP2CurrCmdOP == D3DDP2OP_RENDERSTATE)
|
|
{ // Last instruction is a renderstate, append this one to it
|
|
if (dwDP2CommandLength + sizeof(D3DHAL_DP2RENDERSTATE) <= dwDP2CommandBufSize)
|
|
{
|
|
LPD3DHAL_DP2RENDERSTATE lpRState = (LPD3DHAL_DP2RENDERSTATE)((LPBYTE)lpvDP2Commands +
|
|
dwDP2CommandLength + dp2data.dwCommandOffset);
|
|
lpDP2CurrCommand->wStateCount = ++wDP2CurrCmdCnt;
|
|
lpRState->RenderState = dwStateType;
|
|
lpRState->dwState = value;
|
|
dwDP2CommandLength += sizeof(D3DHAL_DP2RENDERSTATE);
|
|
D3D_INFO(6, "Modify Ins:%08lx", *(LPDWORD)lpDP2CurrCommand);
|
|
return ret;
|
|
}
|
|
}
|
|
// Check for space
|
|
if (dwDP2CommandLength + sizeof(D3DHAL_DP2COMMAND) +
|
|
sizeof(D3DHAL_DP2RENDERSTATE) > dwDP2CommandBufSize)
|
|
{
|
|
ret = FlushStates();
|
|
|
|
// Since we ran out of space, we were not able to put (dwStateType, value)
|
|
// into the batch so rstates will reflect only the last batched
|
|
// renderstate (since the driver updates rstates from the batch).
|
|
// To fix this, we simply put the current (dwStateType, value) into rstates.
|
|
this->rstates[dwStateType]=value;
|
|
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("Error trying to render batched commands in SetRenderStateI");
|
|
return ret;
|
|
}
|
|
}
|
|
// Add new renderstate instruction
|
|
lpDP2CurrCommand = (LPD3DHAL_DP2COMMAND)((LPBYTE)lpvDP2Commands +
|
|
dwDP2CommandLength + dp2data.dwCommandOffset);
|
|
lpDP2CurrCommand->bCommand = D3DDP2OP_RENDERSTATE;
|
|
bDP2CurrCmdOP = D3DDP2OP_RENDERSTATE;
|
|
lpDP2CurrCommand->bReserved = 0;
|
|
lpDP2CurrCommand->wStateCount = 1;
|
|
wDP2CurrCmdCnt = 1;
|
|
D3D_INFO(6, "Write Ins:%08lx", *(LPDWORD)lpDP2CurrCommand);
|
|
// Add renderstate data
|
|
LPD3DHAL_DP2RENDERSTATE lpRState = (LPD3DHAL_DP2RENDERSTATE)(lpDP2CurrCommand + 1);
|
|
lpRState->RenderState = dwStateType;
|
|
lpRState->dwState = value;
|
|
dwDP2CommandLength += sizeof(D3DHAL_DP2COMMAND) + sizeof(D3DHAL_DP2RENDERSTATE);
|
|
return ret;
|
|
}
|
|
|
|
// Map D3DPRIMITIVETYPE to D3DHAL_DP2OPERATION
|
|
const iprim2cmdop[] = {
|
|
0, // Invalid
|
|
0, // Points are invalid too
|
|
D3DDP2OP_INDEXEDLINELIST2,
|
|
D3DDP2OP_INDEXEDLINESTRIP,
|
|
D3DDP2OP_INDEXEDTRIANGLELIST2,
|
|
D3DDP2OP_INDEXEDTRIANGLESTRIP,
|
|
D3DDP2OP_INDEXEDTRIANGLEFAN
|
|
};
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::DrawIndexPrim"
|
|
|
|
//---------------------------------------------------------------------
|
|
//
|
|
// The vertices are already in the vertex buffer.
|
|
//
|
|
HRESULT CDirect3DDeviceIDP2::DrawIndexPrim()
|
|
{
|
|
HRESULT ret = D3D_OK;
|
|
DWORD dwByteCount; // Command length plus indices
|
|
DWORD dwIndicesByteCount; // Indices only
|
|
if(this->dwFEFlags & D3DFE_NEED_TEXTURE_UPDATE)
|
|
{
|
|
ret = UpdateTextures();
|
|
if(ret != D3D_OK)
|
|
{
|
|
D3D_ERR("UpdateTextures failed. Device probably doesn't support current texture (check return code).");
|
|
return ret;
|
|
}
|
|
this->dwFEFlags &= ~D3DFE_NEED_TEXTURE_UPDATE;
|
|
}
|
|
dwIndicesByteCount = sizeof(WORD) * this->dwNumIndices;
|
|
dwByteCount = dwIndicesByteCount + sizeof(D3DHAL_DP2COMMAND) +
|
|
sizeof(D3DHAL_DP2STARTVERTEX);
|
|
|
|
if (dwDP2CommandLength + dwByteCount > dwDP2CommandBufSize)
|
|
{
|
|
// Request the driver to grow the command buffer upon flush
|
|
dp2data.dwReqCommandBufSize = dwByteCount;
|
|
dp2data.dwFlags |= D3DHALDP2_REQCOMMANDBUFSIZE;
|
|
ret = FlushStates(true);
|
|
dp2data.dwFlags &= ~D3DHALDP2_REQCOMMANDBUFSIZE;
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
// Check if the driver did give us what we need or do it ourselves
|
|
ret = GrowCommandBuffer(this->lpDirect3DI, dwByteCount);
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("Could not grow Command Buffer");
|
|
return ret;
|
|
}
|
|
}
|
|
// Insert indexed primitive instruction
|
|
lpDP2CurrCommand = (LPD3DHAL_DP2COMMAND)((LPBYTE)lpvDP2Commands +
|
|
dwDP2CommandLength + dp2data.dwCommandOffset);
|
|
lpDP2CurrCommand->bReserved = 0;
|
|
lpDP2CurrCommand->wPrimitiveCount = (WORD)this->dwNumPrimitives;
|
|
|
|
LPBYTE pIndices = (BYTE*)(lpDP2CurrCommand + 1); // Place for indices
|
|
lpDP2CurrCommand->bCommand = (BYTE)iprim2cmdop[this->primType];
|
|
((LPD3DHAL_DP2STARTVERTEX)(lpDP2CurrCommand+1))->wVStart =
|
|
(WORD)this->dwVertexBase;
|
|
pIndices += sizeof(D3DHAL_DP2STARTVERTEX);
|
|
|
|
#if DBG
|
|
if (lpDP2CurrCommand->bCommand == 0)
|
|
{
|
|
D3D_ERR("Illegal primitive type");
|
|
return DDERR_GENERIC;
|
|
}
|
|
#endif
|
|
bDP2CurrCmdOP = lpDP2CurrCommand->bCommand;
|
|
|
|
memcpy(pIndices, this->lpwIndices, dwIndicesByteCount);
|
|
|
|
wDP2CurrCmdCnt = lpDP2CurrCommand->wPrimitiveCount;
|
|
dwDP2CommandLength += dwByteCount;
|
|
return ret;
|
|
}
|
|
|
|
// Map D3DPRIMITIVETYPE to D3DHAL_DP2OPERATION
|
|
const prim2cmdop[] = {
|
|
0, // Invalid
|
|
D3DDP2OP_POINTS,
|
|
D3DDP2OP_LINELIST,
|
|
D3DDP2OP_LINESTRIP,
|
|
D3DDP2OP_TRIANGLELIST,
|
|
D3DDP2OP_TRIANGLESTRIP,
|
|
D3DDP2OP_TRIANGLEFAN
|
|
};
|
|
// Map D3DPRIMITIVETYPE to bytes needed in command stream
|
|
const prim2cmdsz[] = {
|
|
0, // Invalid
|
|
sizeof(D3DHAL_DP2COMMAND) + sizeof(D3DHAL_DP2POINTS),
|
|
sizeof(D3DHAL_DP2COMMAND) + sizeof(D3DHAL_DP2LINELIST),
|
|
sizeof(D3DHAL_DP2COMMAND) + sizeof(D3DHAL_DP2LINESTRIP),
|
|
sizeof(D3DHAL_DP2COMMAND) + sizeof(D3DHAL_DP2TRIANGLELIST),
|
|
sizeof(D3DHAL_DP2COMMAND) + sizeof(D3DHAL_DP2TRIANGLESTRIP),
|
|
sizeof(D3DHAL_DP2COMMAND) + sizeof(D3DHAL_DP2TRIANGLEFAN)
|
|
};
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::DrawClippedPrim"
|
|
|
|
//---------------------------------------------------------------------
|
|
// This primitive is generated by the clipper.
|
|
// The vertices of this primitive are pointed to by the
|
|
// lpvOut member, which need to be copied into the
|
|
// command stream immediately after the command itself.
|
|
HRESULT CDirect3DDeviceIDP2::DrawClippedPrim()
|
|
{
|
|
HRESULT ret = D3D_OK;
|
|
if(this->dwFEFlags & D3DFE_NEED_TEXTURE_UPDATE)
|
|
{
|
|
ret = UpdateTextures();
|
|
if(ret != D3D_OK)
|
|
{
|
|
D3D_ERR("UpdateTextures failed. Device probably doesn't support current texture (check return code).");
|
|
return ret;
|
|
}
|
|
this->dwFEFlags &= ~D3DFE_NEED_TEXTURE_UPDATE;
|
|
}
|
|
DWORD dwExtra = 0;
|
|
LPVOID lpvVerticesImm; // Place for vertices
|
|
DWORD dwVertexPoolSize = this->dwNumVertices * this->dwOutputSize;
|
|
if (this->primType == D3DPT_TRIANGLEFAN)
|
|
{
|
|
if (rstates[D3DRENDERSTATE_FILLMODE] == D3DFILL_WIREFRAME &&
|
|
this->dwFlags & D3DPV_NONCLIPPED)
|
|
{
|
|
// For unclipped (but pretended to be clipped) tri fans in
|
|
// wireframe mode we generate 3-vertex tri fans to enable drawing of
|
|
// interior edges
|
|
BYTE vertices[__MAX_VERTEX_SIZE*3];
|
|
BYTE *pV1 = vertices + this->dwOutputSize;
|
|
BYTE *pV2 = pV1 + this->dwOutputSize;
|
|
BYTE *pInput = (BYTE*)this->lpvOut;
|
|
memcpy(vertices, pInput, this->dwOutputSize);
|
|
pInput += this->dwOutputSize;
|
|
const DWORD nTriangles = this->dwNumVertices - 2;
|
|
this->dwNumVertices = 3;
|
|
this->dwNumPrimitives = 1;
|
|
this->lpvOut = vertices;
|
|
this->dwFlags &= ~D3DPV_NONCLIPPED; // Remove this flag for recursive call
|
|
for (DWORD i = nTriangles; i; i--)
|
|
{
|
|
memcpy(pV1, pInput, this->dwOutputSize);
|
|
memcpy(pV2, pInput+this->dwOutputSize, this->dwOutputSize);
|
|
pInput += this->dwOutputSize;
|
|
// To enable all edge flag we set the fill mode to SOLID.
|
|
// This will prevent checking the clip flags in the clipper state.
|
|
rstates[D3DRENDERSTATE_FILLMODE] = D3DFILL_SOLID;
|
|
ret = DrawClippedPrim();
|
|
rstates[D3DRENDERSTATE_FILLMODE] = D3DFILL_WIREFRAME;
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
}
|
|
return D3D_OK;
|
|
}
|
|
dwExtra = sizeof(D3DHAL_DP2TRIANGLEFAN_IMM);
|
|
}
|
|
DWORD dwPad = (sizeof(D3DHAL_DP2COMMAND) + dwDP2CommandLength + dwExtra) & 3;
|
|
DWORD dwByteCount = sizeof(D3DHAL_DP2COMMAND) + dwPad + dwExtra + dwVertexPoolSize;
|
|
|
|
// Check for space in the command buffer for commands & vertices
|
|
if (dwDP2CommandLength + dwByteCount > dwDP2CommandBufSize)
|
|
{
|
|
// Flush the current batch but hold on to the vertices
|
|
ret = FlushStates(true);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
if (dwByteCount > dwDP2CommandBufSize)
|
|
{
|
|
ret = GrowCommandBuffer(this->lpDirect3DI, dwByteCount);
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("Could not grow Command Buffer");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
dwPad = (sizeof(D3DHAL_DP2COMMAND) + dwExtra) & 3;
|
|
dwByteCount = sizeof(D3DHAL_DP2COMMAND) + dwExtra + dwPad + dwVertexPoolSize;
|
|
}
|
|
lpDP2CurrCommand = (LPD3DHAL_DP2COMMAND)((LPBYTE)lpvDP2Commands +
|
|
dwDP2CommandLength + dp2data.dwCommandOffset);
|
|
lpDP2CurrCommand->wPrimitiveCount = (WORD)this->dwNumPrimitives;
|
|
lpDP2CurrCommand->bReserved = 0;
|
|
if (this->primType == D3DPT_TRIANGLEFAN)
|
|
{
|
|
// Insert inline instruction and vertices
|
|
bDP2CurrCmdOP = D3DDP2OP_TRIANGLEFAN_IMM;
|
|
lpDP2CurrCommand->bCommand = bDP2CurrCmdOP;
|
|
LPD3DHAL_DP2TRIANGLEFAN_IMM lpTriFanImm = (LPD3DHAL_DP2TRIANGLEFAN_IMM)(lpDP2CurrCommand + 1);
|
|
if (rstates[D3DRENDERSTATE_FILLMODE] == D3DFILL_WIREFRAME)
|
|
{
|
|
lpTriFanImm->dwEdgeFlags = 0;
|
|
ClipVertex **clip = this->ClipperState.current_vbuf;
|
|
// Look at the exterior edges and mark the visible ones
|
|
for(DWORD i = 0; i < this->dwNumVertices; ++i)
|
|
{
|
|
if (clip[i]->clip & CLIPPED_ENABLE)
|
|
lpTriFanImm->dwEdgeFlags |= (1 << i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Mark all exterior edges visible
|
|
lpTriFanImm->dwEdgeFlags = 0xFFFFFFFF;
|
|
}
|
|
lpvVerticesImm = (LPBYTE)(lpTriFanImm + 1) + dwPad;
|
|
}
|
|
else
|
|
{
|
|
// Insert inline instruction and vertices
|
|
bDP2CurrCmdOP = D3DDP2OP_LINELIST_IMM;
|
|
lpDP2CurrCommand->bCommand = bDP2CurrCmdOP;
|
|
lpvVerticesImm = (LPBYTE)(lpDP2CurrCommand + 1) + dwPad;
|
|
}
|
|
memcpy(lpvVerticesImm, this->lpvOut, dwVertexPoolSize);
|
|
dwDP2CommandLength += dwByteCount;
|
|
return ret;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
HRESULT CDirect3DDeviceIDP2::DrawPrim()
|
|
{
|
|
HRESULT ret = D3D_OK;
|
|
if(this->dwFEFlags & D3DFE_NEED_TEXTURE_UPDATE)
|
|
{
|
|
ret = UpdateTextures();
|
|
if(ret != D3D_OK)
|
|
{
|
|
D3D_ERR("UpdateTextures failed. Device probably doesn't support current texture (check return code).");
|
|
return ret;
|
|
}
|
|
this->dwFEFlags &= ~D3DFE_NEED_TEXTURE_UPDATE;
|
|
}
|
|
// Check for space in the command buffer for new command.
|
|
// The vertices are already in the vertex buffer.
|
|
if (dwDP2CommandLength + prim2cmdsz[this->primType] > dwDP2CommandBufSize)
|
|
{
|
|
ret = FlushStates(true);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
}
|
|
// Insert non indexed primitive instruction
|
|
lpDP2CurrCommand = (LPD3DHAL_DP2COMMAND)((LPBYTE)lpvDP2Commands +
|
|
dwDP2CommandLength + dp2data.dwCommandOffset);
|
|
bDP2CurrCmdOP = (BYTE)prim2cmdop[this->primType];
|
|
lpDP2CurrCommand->bCommand = bDP2CurrCmdOP;
|
|
lpDP2CurrCommand->bReserved = 0;
|
|
if (bDP2CurrCmdOP == D3DDP2OP_POINTS)
|
|
{
|
|
wDP2CurrCmdCnt = 1;
|
|
LPD3DHAL_DP2POINTS lpPoints = (LPD3DHAL_DP2POINTS)(lpDP2CurrCommand + 1);
|
|
lpPoints->wCount = (WORD)this->dwNumVertices;
|
|
lpPoints->wVStart = (WORD)this->dwVertexBase;
|
|
}
|
|
else
|
|
{
|
|
// Linestrip, trianglestrip, trianglefan, linelist and trianglelist are identical
|
|
wDP2CurrCmdCnt = (WORD)this->dwNumPrimitives;
|
|
LPD3DHAL_DP2LINESTRIP lpStrip = (LPD3DHAL_DP2LINESTRIP)(lpDP2CurrCommand + 1);
|
|
lpStrip->wVStart = (WORD)this->dwVertexBase;
|
|
}
|
|
lpDP2CurrCommand->wPrimitiveCount = wDP2CurrCmdCnt;
|
|
dwDP2CommandLength += prim2cmdsz[this->primType];
|
|
#ifdef VALIDATE_DP2CMD
|
|
ValidateCommand(lpDP2CurrCommand);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::SetTSSI"
|
|
|
|
HRESULT CDirect3DDeviceIDP2::SetTSSI(DWORD dwStage, D3DTEXTURESTAGESTATETYPE dwState, DWORD dwValue)
|
|
{
|
|
HRESULT ret = D3D_OK;
|
|
|
|
// Filter unsupported states
|
|
if (dwState >= m_tssMax)
|
|
return D3D_OK;
|
|
|
|
if (bDP2CurrCmdOP == D3DDP2OP_TEXTURESTAGESTATE)
|
|
{ // Last instruction is a texture stage state, append this one to it
|
|
if (dwDP2CommandLength + sizeof(D3DHAL_DP2TEXTURESTAGESTATE) <= dwDP2CommandBufSize)
|
|
{
|
|
LPD3DHAL_DP2TEXTURESTAGESTATE lpRState = (LPD3DHAL_DP2TEXTURESTAGESTATE)((LPBYTE)lpvDP2Commands +
|
|
dwDP2CommandLength + dp2data.dwCommandOffset);
|
|
lpDP2CurrCommand->wStateCount = ++wDP2CurrCmdCnt;
|
|
lpRState->wStage = (WORD)dwStage;
|
|
lpRState->TSState = (WORD)dwState;
|
|
lpRState->dwValue = dwValue;
|
|
dwDP2CommandLength += sizeof(D3DHAL_DP2TEXTURESTAGESTATE);
|
|
D3D_INFO(6, "Modify Ins:%08lx", *(LPDWORD)lpDP2CurrCommand);
|
|
return ret;
|
|
}
|
|
}
|
|
// Check for space
|
|
if (dwDP2CommandLength + sizeof(D3DHAL_DP2COMMAND) +
|
|
sizeof(D3DHAL_DP2TEXTURESTAGESTATE) > dwDP2CommandBufSize)
|
|
{
|
|
ret = FlushStates();
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("Error trying to render batched commands in SetTSSI");
|
|
return ret;
|
|
}
|
|
}
|
|
// Add new renderstate instruction
|
|
lpDP2CurrCommand = (LPD3DHAL_DP2COMMAND)((LPBYTE)lpvDP2Commands +
|
|
dwDP2CommandLength + dp2data.dwCommandOffset);
|
|
lpDP2CurrCommand->bCommand = D3DDP2OP_TEXTURESTAGESTATE;
|
|
bDP2CurrCmdOP = D3DDP2OP_TEXTURESTAGESTATE;
|
|
lpDP2CurrCommand->bReserved = 0;
|
|
lpDP2CurrCommand->wStateCount = 1;
|
|
wDP2CurrCmdCnt = 1;
|
|
D3D_INFO(6, "Write Ins:%08lx", *(LPDWORD)lpDP2CurrCommand);
|
|
// Add renderstate data
|
|
LPD3DHAL_DP2TEXTURESTAGESTATE lpRState = (LPD3DHAL_DP2TEXTURESTAGESTATE)(lpDP2CurrCommand + 1);
|
|
lpRState->wStage = (WORD)dwStage;
|
|
lpRState->TSState = (WORD)dwState;
|
|
lpRState->dwValue = dwValue;
|
|
dwDP2CommandLength += sizeof(D3DHAL_DP2COMMAND) + sizeof(D3DHAL_DP2TEXTURESTAGESTATE);
|
|
return ret;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::ValidateDevice"
|
|
|
|
HRESULT D3DAPI
|
|
CDirect3DDeviceIDP2::ValidateDevice(LPDWORD lpdwNumPasses)
|
|
{
|
|
try
|
|
{
|
|
// Holds D3D lock until exit.
|
|
CLockD3DMT ldmLock(this, DPF_MODNAME, REMIND(""));
|
|
HRESULT ret;
|
|
D3DHAL_VALIDATETEXTURESTAGESTATEDATA vbod;
|
|
|
|
if (!VALID_DIRECT3DDEVICE_PTR(this))
|
|
{
|
|
D3D_ERR( "Invalid Direct3DDevice7 pointer" );
|
|
return DDERR_INVALIDOBJECT;
|
|
}
|
|
if (!VALID_PTR(lpdwNumPasses, sizeof(DWORD)))
|
|
{
|
|
D3D_ERR( "Invalid lpdwNumPasses pointer" );
|
|
return DDERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// First, Update textures since drivers pass /fail this call based
|
|
// on the current texture handles
|
|
ret = UpdateTextures();
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("Error trying to update managed textures in ValidateDevice");
|
|
return ret;
|
|
}
|
|
// First, flush states, so we can validate the current state
|
|
ret = FlushStates();
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("Error trying to FlushStates in ValidateDevice");
|
|
return ret;
|
|
}
|
|
|
|
// Now ask the driver!
|
|
|
|
*lpdwNumPasses = 0;
|
|
memset(&vbod, 0, sizeof(D3DHAL_VALIDATETEXTURESTAGESTATEDATA));
|
|
vbod.dwhContext = this->dwhContext;
|
|
if (this->lpD3DHALCallbacks3->ValidateTextureStageState)
|
|
{
|
|
CALL_HAL3ONLY(ret, this, ValidateTextureStageState, &vbod);
|
|
if (ret != DDHAL_DRIVER_HANDLED)
|
|
return DDERR_UNSUPPORTED;
|
|
|
|
*lpdwNumPasses = vbod.dwNumPasses;
|
|
return vbod.ddrval;
|
|
}
|
|
else
|
|
{
|
|
D3D_ERR("Error: ValidateTextureStageState not supported by the driver.");
|
|
}
|
|
|
|
return DDERR_UNSUPPORTED;
|
|
}
|
|
catch (HRESULT ret)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::StartPrimVB"
|
|
//---------------------------------------------------------------------
|
|
// This function prepares the batch for new primitive.
|
|
// Called only if vertices from user memory are NOT used for rendering
|
|
//
|
|
HRESULT CDirect3DDeviceIDP2::StartPrimVB(LPDIRECT3DVERTEXBUFFERI lpVBI,
|
|
DWORD dwStartVertex)
|
|
{
|
|
HRESULT ret = D3D_OK;
|
|
|
|
// If VID has been changed or new vertex buffer is used we flush the batch
|
|
if (this->dwVIDOut != dp2data.dwVertexType ||
|
|
lpDP2CurrBatchVBI != lpVBI ||
|
|
dp2data.lpDDVertex != ((LPDDRAWI_DDRAWSURFACE_INT)(lpVBI->GetDDS()))->lpLcl)
|
|
{
|
|
ret = FlushStates();
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
dp2data.dwVertexType = this->dwVIDOut;
|
|
dp2data.dwVertexSize = this->dwOutputSize;
|
|
dp2data.lpDDVertex = ((LPDDRAWI_DDRAWSURFACE_INT)(lpVBI->GetDDS()))->lpLcl;
|
|
// Release previously used vertex buffer (if any), because we do not
|
|
// need it any more. We did AddRef() to TL buffer, so it is safe.
|
|
if (lpDP2CurrBatchVBI)
|
|
{
|
|
lpDP2CurrBatchVBI->lpDevIBatched = NULL;
|
|
lpDP2CurrBatchVBI->Release();
|
|
}
|
|
// If a vertex buffer is used for rendering, make sure that it is not
|
|
// released by user. So do AddRef().
|
|
lpDP2CurrBatchVBI = lpVBI;
|
|
lpDP2CurrBatchVBI->AddRef();
|
|
}
|
|
if (this->TLVbuf_GetVBI() == lpVBI)
|
|
{
|
|
this->dwVertexBase = this->dwDP2VertexCount;
|
|
DDASSERT(this->dwVertexBase < MAX_DX6_VERTICES);
|
|
dp2data.dwFlags |= D3DHALDP2_SWAPVERTEXBUFFER;
|
|
this->dwDP2VertexCount = this->dwVertexBase + this->dwNumVertices;
|
|
#ifdef VTABLE_HACK
|
|
VtblDrawPrimitiveVBDefault();
|
|
VtblDrawIndexedPrimitiveVBDefault();
|
|
#endif VTABLE_HACK
|
|
}
|
|
else
|
|
{
|
|
this->dwVertexBase = dwStartVertex;
|
|
dp2data.dwFlags &= ~D3DHALDP2_SWAPVERTEXBUFFER;
|
|
this->dwDP2VertexCount = max(this->dwDP2VertexCount, this->dwVertexBase + this->dwNumVertices);
|
|
#ifdef VTABLE_HACK
|
|
VtblDrawPrimitiveDefault();
|
|
VtblDrawIndexedPrimitiveDefault();
|
|
#endif VTABLE_HACK
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::StartPrimUserMem"
|
|
//---------------------------------------------------------------------
|
|
// This function prepares the batch for new primitive.
|
|
// Called if vertices from user memory is used for rendering
|
|
//
|
|
HRESULT CDirect3DDeviceIDP2::StartPrimUserMem(LPVOID lpMem)
|
|
{
|
|
HRESULT ret = D3D_OK;
|
|
// We fail vid mem VB for clipping
|
|
bool bWriteOnly = ((this->dwDeviceFlags & D3DDEV_DONOTCLIP) || IS_TLHAL_DEVICE(this))!=0;
|
|
|
|
// If the primitive is small, we copy vertices into the TL buffer
|
|
// ATTENTION: Dont do this if the device is a TL device ?
|
|
if (this->dwNumVertices < LOWVERTICESNUMBER)
|
|
{
|
|
if (this->dwVertexPoolSize > this->TLVbuf_GetSize())
|
|
{
|
|
if (this->TLVbuf_Grow(this->dwVertexPoolSize, bWriteOnly) != D3D_OK)
|
|
{
|
|
D3D_ERR( "Could not grow TL vertex buffer" );
|
|
return DDERR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
// So now user memory is not used any more.
|
|
ret = StartPrimVB(this->TLVbuf_GetVBI(), 0);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
LPVOID tmp = this->TLVbuf_GetAddress();
|
|
memcpy(tmp, this->lpvOut, this->dwVertexPoolSize);
|
|
// We have to update lpvOut, because it was set to user memory
|
|
this->lpvOut = tmp;
|
|
}
|
|
else
|
|
{
|
|
// We can not mix user memory primitive with other primitives, so
|
|
// flush the batch.
|
|
// Do not forget to flush the batch after rendering this primitive
|
|
ret = this->FlushStates();
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
// Release previously used vertex buffer (if any), because we do not
|
|
// it any more
|
|
if (lpDP2CurrBatchVBI)
|
|
{
|
|
lpDP2CurrBatchVBI->lpDevIBatched = NULL;
|
|
lpDP2CurrBatchVBI->Release();
|
|
lpDP2CurrBatchVBI = NULL;
|
|
#ifdef VTABLE_HACK
|
|
VtblDrawPrimitiveVBDefault();
|
|
VtblDrawIndexedPrimitiveVBDefault();
|
|
VtblDrawPrimitiveDefault();
|
|
VtblDrawIndexedPrimitiveDefault();
|
|
#endif VTABLE_HACK
|
|
}
|
|
dp2data.dwVertexType = this->dwVIDOut;
|
|
dp2data.dwVertexSize = this->dwOutputSize;
|
|
dp2data.lpVertices = lpMem;
|
|
dp2data.dwFlags |= D3DHALDP2_USERMEMVERTICES;
|
|
dp2data.dwFlags &= ~D3DHALDP2_SWAPVERTEXBUFFER;
|
|
this->dwDP2VertexCount = this->dwNumVertices;
|
|
this->dwFlags |= D3DPV_USERMEMVERTICES;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::EndPrim"
|
|
//---------------------------------------------------------------------
|
|
// This function should not be called from DrawVertexBufferVB
|
|
//
|
|
HRESULT CDirect3DDeviceIDP2::EndPrim()
|
|
{
|
|
HRESULT ret = D3D_OK;
|
|
if (this->dwFlags & D3DPV_USERMEMVERTICES)
|
|
// We can not mix user memory primitive, so flush it.
|
|
ret = this->FlushStates();
|
|
else
|
|
{
|
|
// If TL buffer was used, we have to move its internal base pointer
|
|
this->TLVbuf_Base() += this->dwVertexPoolSize;
|
|
DDASSERT(TLVbuf_base <= TLVbuf_size);
|
|
DDASSERT(TLVbuf_base == this->dwDP2VertexCount * this->dwOutputSize);
|
|
}
|
|
|
|
this->dwFlags &= ~D3DPV_USERMEMVERTICES;
|
|
return ret;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
//
|
|
//
|
|
void CDirect3DDeviceIDP2::UpdateDrvViewInfo(LPD3DVIEWPORT7 lpVwpData)
|
|
{
|
|
LPD3DHAL_DP2VIEWPORTINFO pData;
|
|
pData = (LPD3DHAL_DP2VIEWPORTINFO)GetHalBufferPointer(D3DDP2OP_VIEWPORTINFO, sizeof(*pData));
|
|
pData->dwX = lpVwpData->dwX;
|
|
pData->dwY = lpVwpData->dwY;
|
|
pData->dwWidth = lpVwpData->dwWidth;
|
|
pData->dwHeight = lpVwpData->dwHeight;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
//
|
|
//
|
|
void CDirect3DDeviceIDP2::UpdateDrvWInfo()
|
|
{
|
|
LPD3DHAL_DP2WINFO pData;
|
|
pData = (LPD3DHAL_DP2WINFO)GetHalBufferPointer(D3DDP2OP_WINFO, sizeof(*pData));
|
|
D3DMATRIXI &m = transform.proj;
|
|
if( (m._33 == m._34) || (m._33 == 0.0f) )
|
|
{
|
|
D3D_WARN(1, "Cannot compute WNear and WFar from the supplied projection matrix.\n Setting wNear to 0.0 and wFar to 1.0" );
|
|
pData->dvWNear = 0.0f;
|
|
pData->dvWFar = 1.0f;
|
|
return;
|
|
}
|
|
|
|
pData->dvWNear = m._44 - m._43/m._33*m._34;
|
|
pData->dvWFar = (m._44 - m._43)/(m._33 - m._34)*m._34 + m._44;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Initializes command header in the DP2 command buffer,
|
|
// reserves space for the command data and returns pointer to the command
|
|
// data
|
|
//
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::GetHalBufferPointer"
|
|
|
|
LPVOID CDirect3DDeviceIDP2::GetHalBufferPointer(D3DHAL_DP2OPERATION op, DWORD dwDataSize)
|
|
{
|
|
DWORD dwCommandSize = sizeof(D3DHAL_DP2COMMAND) + dwDataSize;
|
|
|
|
// Check to see if there is space to add a new command for space
|
|
if (dwCommandSize + dwDP2CommandLength > dwDP2CommandBufSize)
|
|
{
|
|
HRESULT ret = FlushStates();
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("Error trying to render batched commands in GetHalBufferPointer");
|
|
throw ret;
|
|
}
|
|
}
|
|
lpDP2CurrCommand = (LPD3DHAL_DP2COMMAND)((LPBYTE)lpvDP2Commands +
|
|
dwDP2CommandLength + dp2data.dwCommandOffset);
|
|
lpDP2CurrCommand->bCommand = op;
|
|
bDP2CurrCmdOP = op;
|
|
lpDP2CurrCommand->bReserved = 0;
|
|
lpDP2CurrCommand->wStateCount = 1;
|
|
wDP2CurrCmdCnt = 1;
|
|
|
|
dwDP2CommandLength += dwCommandSize;
|
|
return (LPVOID)(lpDP2CurrCommand + 1);
|
|
}
|
|
//---------------------------------------------------------------------
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::UpdateDriverStates"
|
|
|
|
HRESULT
|
|
CDirect3DDeviceIDP2::UpdateDriverStates()
|
|
{
|
|
static D3DRENDERSTATETYPE dp2states[] =
|
|
{
|
|
D3DRENDERSTATE_SPECULARENABLE,
|
|
D3DRENDERSTATE_ZENABLE,
|
|
D3DRENDERSTATE_FILLMODE,
|
|
D3DRENDERSTATE_SHADEMODE,
|
|
D3DRENDERSTATE_LINEPATTERN,
|
|
D3DRENDERSTATE_ZWRITEENABLE,
|
|
D3DRENDERSTATE_ALPHATESTENABLE,
|
|
D3DRENDERSTATE_LASTPIXEL,
|
|
D3DRENDERSTATE_SRCBLEND,
|
|
D3DRENDERSTATE_DESTBLEND,
|
|
D3DRENDERSTATE_CULLMODE,
|
|
D3DRENDERSTATE_ZFUNC,
|
|
D3DRENDERSTATE_ALPHAREF,
|
|
D3DRENDERSTATE_ALPHAFUNC,
|
|
D3DRENDERSTATE_DITHERENABLE,
|
|
D3DRENDERSTATE_FOGENABLE,
|
|
D3DRENDERSTATE_ZVISIBLE,
|
|
D3DRENDERSTATE_STIPPLEDALPHA,
|
|
D3DRENDERSTATE_FOGCOLOR,
|
|
D3DRENDERSTATE_FOGTABLEMODE,
|
|
D3DRENDERSTATE_FOGSTART,
|
|
D3DRENDERSTATE_FOGEND,
|
|
D3DRENDERSTATE_FOGDENSITY,
|
|
D3DRENDERSTATE_COLORKEYENABLE,
|
|
D3DRENDERSTATE_ALPHABLENDENABLE,
|
|
D3DRENDERSTATE_ZBIAS,
|
|
D3DRENDERSTATE_RANGEFOGENABLE,
|
|
D3DRENDERSTATE_STIPPLEENABLE,
|
|
D3DRENDERSTATE_MONOENABLE,
|
|
D3DRENDERSTATE_ROP2,
|
|
D3DRENDERSTATE_PLANEMASK,
|
|
D3DRENDERSTATE_WRAPU,
|
|
D3DRENDERSTATE_WRAPV,
|
|
D3DRENDERSTATE_ANTIALIAS,
|
|
D3DRENDERSTATE_SUBPIXEL,
|
|
D3DRENDERSTATE_SUBPIXELX,
|
|
D3DRENDERSTATE_EDGEANTIALIAS,
|
|
D3DRENDERSTATE_STIPPLEPATTERN00,
|
|
D3DRENDERSTATE_STIPPLEPATTERN01,
|
|
D3DRENDERSTATE_STIPPLEPATTERN02,
|
|
D3DRENDERSTATE_STIPPLEPATTERN03,
|
|
D3DRENDERSTATE_STIPPLEPATTERN04,
|
|
D3DRENDERSTATE_STIPPLEPATTERN05,
|
|
D3DRENDERSTATE_STIPPLEPATTERN06,
|
|
D3DRENDERSTATE_STIPPLEPATTERN07,
|
|
D3DRENDERSTATE_STIPPLEPATTERN08,
|
|
D3DRENDERSTATE_STIPPLEPATTERN09,
|
|
D3DRENDERSTATE_STIPPLEPATTERN10,
|
|
D3DRENDERSTATE_STIPPLEPATTERN11,
|
|
D3DRENDERSTATE_STIPPLEPATTERN12,
|
|
D3DRENDERSTATE_STIPPLEPATTERN13,
|
|
D3DRENDERSTATE_STIPPLEPATTERN14,
|
|
D3DRENDERSTATE_STIPPLEPATTERN15,
|
|
D3DRENDERSTATE_STIPPLEPATTERN16,
|
|
D3DRENDERSTATE_STIPPLEPATTERN17,
|
|
D3DRENDERSTATE_STIPPLEPATTERN18,
|
|
D3DRENDERSTATE_STIPPLEPATTERN19,
|
|
D3DRENDERSTATE_STIPPLEPATTERN20,
|
|
D3DRENDERSTATE_STIPPLEPATTERN21,
|
|
D3DRENDERSTATE_STIPPLEPATTERN22,
|
|
D3DRENDERSTATE_STIPPLEPATTERN23,
|
|
D3DRENDERSTATE_STIPPLEPATTERN24,
|
|
D3DRENDERSTATE_STIPPLEPATTERN25,
|
|
D3DRENDERSTATE_STIPPLEPATTERN26,
|
|
D3DRENDERSTATE_STIPPLEPATTERN27,
|
|
D3DRENDERSTATE_STIPPLEPATTERN28,
|
|
D3DRENDERSTATE_STIPPLEPATTERN29,
|
|
D3DRENDERSTATE_STIPPLEPATTERN30,
|
|
D3DRENDERSTATE_STIPPLEPATTERN31,
|
|
D3DRENDERSTATE_TEXTUREPERSPECTIVE,
|
|
D3DRENDERSTATE_STENCILENABLE,
|
|
D3DRENDERSTATE_STENCILFAIL,
|
|
D3DRENDERSTATE_STENCILZFAIL,
|
|
D3DRENDERSTATE_STENCILPASS,
|
|
D3DRENDERSTATE_STENCILFUNC,
|
|
D3DRENDERSTATE_STENCILREF,
|
|
D3DRENDERSTATE_STENCILMASK,
|
|
D3DRENDERSTATE_STENCILWRITEMASK,
|
|
D3DRENDERSTATE_TEXTUREFACTOR,
|
|
D3DRENDERSTATE_WRAP0,
|
|
D3DRENDERSTATE_WRAP1,
|
|
D3DRENDERSTATE_WRAP2,
|
|
D3DRENDERSTATE_WRAP3,
|
|
D3DRENDERSTATE_WRAP4,
|
|
D3DRENDERSTATE_WRAP5,
|
|
D3DRENDERSTATE_WRAP6,
|
|
D3DRENDERSTATE_WRAP7
|
|
};
|
|
HRESULT ret;
|
|
for (DWORD i=0;i<sizeof(dp2states)/sizeof(D3DRENDERSTATETYPE); ++i)
|
|
{
|
|
ret = this->SetRenderStateI(dp2states[i], this->rstates[dp2states[i]]);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
}
|
|
// Update new states
|
|
for (i=0; i<dwMaxTextureBlendStages; ++i)
|
|
for (DWORD j=D3DTSS_COLOROP; j<=D3DTSS_BUMPENVLOFFSET; ++j) // D3DTSS_BUMPENVLOFFSET is the max. TSS understood by a DP2HAL (DX6) driver
|
|
{
|
|
D3D_INFO(6,"Calling SetTSSI(%d,%d,%08lx)",i,j, this->tsstates[i][j]);
|
|
ret = this->SetTSSI(i, (D3DTEXTURESTAGESTATETYPE)j, this->tsstates[i][j]);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
}
|
|
return D3D_OK;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// ProcessPrimitive processes indexed, non-indexed primitives or
|
|
// vertices only as defined by "op"
|
|
//
|
|
// op = __PROCPRIMOP_NONINDEXEDPRIM by default
|
|
//
|
|
HRESULT CDirect3DDeviceIDP2::ProcessPrimitive(__PROCPRIMOP op)
|
|
{
|
|
HRESULT ret=D3D_OK;
|
|
|
|
// Grow clip flags buffer if we need clipping
|
|
//
|
|
if (!(this->dwDeviceFlags & D3DDEV_DONOTCLIP))
|
|
{
|
|
DWORD size = this->dwNumVertices * sizeof(D3DFE_CLIPCODE);
|
|
if (size > this->HVbuf.GetSize())
|
|
{
|
|
if (this->HVbuf.Grow(size) != D3D_OK)
|
|
{
|
|
D3D_ERR( "Could not grow clip buffer" );
|
|
ret = DDERR_OUTOFMEMORY;
|
|
return ret;
|
|
}
|
|
}
|
|
this->lpClipFlags = (D3DFE_CLIPCODE*)this->HVbuf.GetAddress();
|
|
}
|
|
|
|
if (FVF_TRANSFORMED(this->dwVIDIn))
|
|
{
|
|
// Pass vertices directly from the user memory
|
|
this->dwVIDOut = this->dwVIDIn;
|
|
this->dwOutputSize = this->position.dwStride;
|
|
this->lpvOut = this->position.lpvData;
|
|
this->dwVertexPoolSize = this->dwNumVertices * this->dwOutputSize;
|
|
|
|
StartPrimUserMem(this->position.lpvData);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
if (this->dwDeviceFlags & D3DDEV_DONOTCLIP)
|
|
{
|
|
if (!(this->dwDeviceFlags & D3DDEV_DONOTUPDATEEXTENTS))
|
|
D3DFE_updateExtents(this);
|
|
#ifdef VTABLE_HACK
|
|
else
|
|
if (!IS_MT_DEVICE(this) && this->dwNumVertices < LOWVERTICESNUMBER)
|
|
if (op == __PROCPRIMOP_INDEXEDPRIM)
|
|
VtblDrawIndexedPrimitiveTL();
|
|
else
|
|
VtblDrawPrimitiveTL();
|
|
#endif // VTABLE_HACK
|
|
if (op == __PROCPRIMOP_INDEXEDPRIM)
|
|
{
|
|
ret = this->DrawIndexPrim();
|
|
}
|
|
else
|
|
{
|
|
ret = this->DrawPrim();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD clip_intersect = D3DFE_GenClipFlags(this);
|
|
D3DFE_UpdateClipStatus(this);
|
|
if (!clip_intersect)
|
|
{
|
|
this->dwFlags |= D3DPV_TLVCLIP;
|
|
if (op == __PROCPRIMOP_INDEXEDPRIM)
|
|
{
|
|
ret = DoDrawIndexedPrimitive(this);
|
|
}
|
|
else
|
|
{
|
|
ret = DoDrawPrimitive(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->dwVertexPoolSize = this->dwNumVertices * this->dwOutputSize;
|
|
if (op == __PROCPRIMOP_INDEXEDPRIM)
|
|
{
|
|
if ((this->dwDeviceFlags & (D3DDEV_DONOTCLIP | D3DDEV_TLVBUFWRITEONLY))==D3DDEV_TLVBUFWRITEONLY)
|
|
{
|
|
if( FAILED(this->TLVbuf_Grow(this->dwVertexPoolSize, false)) )
|
|
{
|
|
D3D_ERR( "Could not grow TL vertex buffer" );
|
|
return DDERR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else if (this->dwVertexPoolSize > this->TLVbuf_GetSize())
|
|
{
|
|
if (this->TLVbuf_Grow(this->dwVertexPoolSize,
|
|
(this->dwDeviceFlags & D3DDEV_DONOTCLIP)!=0) != D3D_OK)
|
|
{
|
|
D3D_ERR( "Could not grow TL vertex buffer" );
|
|
ret = DDERR_OUTOFMEMORY;
|
|
return ret;
|
|
}
|
|
}
|
|
#ifdef VTABLE_HACK
|
|
// Use fast path if single threaded device and not using strided API
|
|
if (!(IS_MT_DEVICE(this) || (this->dwDeviceFlags & D3DDEV_STRIDE))
|
|
&& IS_FPU_SETUP(this))
|
|
VtblDrawIndexedPrimitiveFE();
|
|
#endif // VTABLE_HACK
|
|
}
|
|
else
|
|
{
|
|
if (this->dwVertexPoolSize > this->TLVbuf_GetSize())
|
|
{
|
|
if (this->TLVbuf_Grow(this->dwVertexPoolSize, true) != D3D_OK)
|
|
{
|
|
D3D_ERR( "Could not grow TL vertex buffer" );
|
|
ret = DDERR_OUTOFMEMORY;
|
|
return ret;
|
|
}
|
|
}
|
|
#ifdef VTABLE_HACK
|
|
// Use fast path if single threaded device and not using strided API
|
|
if (!(IS_MT_DEVICE(this) || (this->dwDeviceFlags & D3DDEV_STRIDE))
|
|
&& IS_FPU_SETUP(this))
|
|
VtblDrawPrimitiveFE();
|
|
#endif // VTABLE_HACK
|
|
}
|
|
|
|
ret = StartPrimVB(this->TLVbuf_GetVBI(), 0);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
this->lpvOut = this->TLVbuf_GetAddress();
|
|
|
|
// Update Lighting and related flags
|
|
DoUpdateState(this);
|
|
|
|
#ifdef VTABLE_HACK
|
|
// Save the flags that can be persisted if state does not change
|
|
this->dwLastFlags = this->dwFlags & D3DPV_PERSIST;
|
|
#endif // VTABLE_HACK
|
|
|
|
// Call PSGP or our implementation
|
|
if (op == __PROCPRIMOP_INDEXEDPRIM)
|
|
{
|
|
ret = this->pGeometryFuncs->ProcessIndexedPrimitive(this);
|
|
}
|
|
else
|
|
{
|
|
ret = this->pGeometryFuncs->ProcessPrimitive(this);
|
|
}
|
|
D3DFE_UpdateClipStatus(this);
|
|
}
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("ProcessPrimitive failed");
|
|
return ret;
|
|
}
|
|
return EndPrim();
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// Growing aligned vertex buffer implementation.
|
|
//
|
|
HRESULT CDirect3DDeviceIDP2::TLVbuf_Grow(DWORD growSize, bool bWriteOnly)
|
|
{
|
|
D3DVERTEXBUFFERDESC vbdesc = {sizeof(D3DVERTEXBUFFERDESC), 0, D3DFVF_TLVERTEX, 0};
|
|
DWORD dwRefCnt = 1;
|
|
DWORD bTLVbufIsCurr = static_cast<CDirect3DVertexBuffer*>(allocatedBuf) == lpDP2CurrBatchVBI; // Is ref cnt of TLVbuf 1 or 2 ?
|
|
|
|
bool bDP2WriteOnly = (this->dwDeviceFlags & D3DDEV_TLVBUFWRITEONLY) != 0;
|
|
// Avoid to many changes. Restrict TLVbuf to sys mem if too many changes
|
|
if (this->dwTLVbufChanges >= D3D_MAX_TLVBUF_CHANGES)
|
|
{
|
|
#if DBG
|
|
if (this->dwTLVbufChanges == D3D_MAX_TLVBUF_CHANGES)
|
|
DPF(1, "Too many changes: Limiting internal VB to sys mem.");
|
|
#endif
|
|
bWriteOnly = false;
|
|
}
|
|
if (TLVbuf_base || (bWriteOnly != bDP2WriteOnly))
|
|
{
|
|
HRESULT ret;
|
|
ret = FlushStatesReq(growSize);
|
|
if (ret != D3D_OK)
|
|
{
|
|
D3D_ERR("Error trying to render batched commands in CDirect3DDeviceIDP2::TLVbuf_Grow");
|
|
return ret;
|
|
}
|
|
TLVbuf_base = 0;
|
|
}
|
|
if (growSize <= TLVbuf_size)
|
|
{
|
|
if (bWriteOnly == bDP2WriteOnly)
|
|
return D3D_OK;
|
|
else
|
|
this->dwTLVbufChanges++;
|
|
}
|
|
if (allocatedBuf)
|
|
{
|
|
allocatedBuf->Release();
|
|
allocatedBuf = NULL;
|
|
}
|
|
if (bTLVbufIsCurr)
|
|
{
|
|
if (lpDP2CurrBatchVBI)
|
|
{
|
|
lpDP2CurrBatchVBI->lpDevIBatched = NULL;
|
|
lpDP2CurrBatchVBI->Release();
|
|
}
|
|
lpDP2CurrBatchVBI = NULL;
|
|
dp2data.lpDDVertex = NULL;
|
|
}
|
|
// Make sure we do not shrink the VB since it will
|
|
// grow it only as large to fit the largest primitive and might not
|
|
// be enough to get good batching perf.
|
|
DWORD size = max(growSize, TLVbuf_size);
|
|
size = (DWORD)max(size, (__INIT_VERTEX_NUMBER*2)*sizeof(D3DTLVERTEX));
|
|
vbdesc.dwNumVertices = (size + 31) / sizeof(D3DTLVERTEX);
|
|
TLVbuf_size = vbdesc.dwNumVertices * sizeof(D3DTLVERTEX);
|
|
if (!IS_HW_DEVICE(this))
|
|
{
|
|
vbdesc.dwCaps = D3DVBCAPS_SYSTEMMEMORY;
|
|
}
|
|
if (bWriteOnly)
|
|
{
|
|
vbdesc.dwCaps |= D3DVBCAPS_WRITEONLY;
|
|
this->dwDeviceFlags |= D3DDEV_TLVBUFWRITEONLY;
|
|
}
|
|
else
|
|
{
|
|
this->dwDeviceFlags &= ~D3DDEV_TLVBUFWRITEONLY;
|
|
}
|
|
vbdesc.dwCaps |= D3DVBCAPS_DONOTCLIP;
|
|
if (this->lpDirect3DI->CreateVertexBufferI(&vbdesc, &allocatedBuf, D3DVBFLAGS_CREATEMULTIBUFFER) != DD_OK)
|
|
{
|
|
// This should fail duirng mode switches or ulta-low memory situations. In either case,
|
|
// we set allocatedBuf to a valid VB object since it gets dereferenced many places without
|
|
// checking for it being NULL. WE use the special "NULL" VB created at init time for just
|
|
// this purpose
|
|
allocatedBuf = pNullVB;
|
|
if (pNullVB)
|
|
{
|
|
allocatedBuf->AddRef();
|
|
if (bTLVbufIsCurr)
|
|
{
|
|
lpDP2CurrBatchVBI = static_cast<CDirect3DVertexBuffer*>(allocatedBuf);
|
|
lpDP2CurrBatchVBI->AddRef();
|
|
dp2data.lpDDVertex = ((LPDDRAWI_DDRAWSURFACE_INT)(lpDP2CurrBatchVBI->GetDDS()))->lpLcl;
|
|
}
|
|
}
|
|
TLVbuf_size = 0;
|
|
alignedBuf = NULL; // Lets see if some one tries to use this...
|
|
D3D_ERR("Could not allocate internal vertex buffer");
|
|
return DDERR_OUTOFMEMORY;
|
|
}
|
|
// Update lpDP2CurrentBatchVBI if necessary
|
|
if (bTLVbufIsCurr)
|
|
{
|
|
lpDP2CurrBatchVBI = static_cast<CDirect3DVertexBuffer*>(allocatedBuf);
|
|
lpDP2CurrBatchVBI->AddRef();
|
|
dp2data.lpDDVertex = ((LPDDRAWI_DDRAWSURFACE_INT)(lpDP2CurrBatchVBI->GetDDS()))->lpLcl;
|
|
}
|
|
if (allocatedBuf->Lock(DDLOCK_WAIT, &alignedBuf, NULL) != DD_OK)
|
|
{
|
|
D3D_ERR("Could not lock internal vertex buffer");
|
|
TLVbuf_size = 0;
|
|
alignedBuf = NULL; // Lets see if some one tries to use this...
|
|
return DDERR_OUTOFMEMORY;
|
|
}
|
|
return D3D_OK;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Computes the following data
|
|
// - dwTextureCoordOffset[] offset of every input texture coordinates
|
|
|
|
static __inline void ComputeInpTexCoordOffsets(DWORD dwNumTexCoord,
|
|
DWORD dwFVF,
|
|
DWORD *pdwTextureCoordOffset)
|
|
{
|
|
// Compute texture coordinate size
|
|
DWORD dwTextureFormats = dwFVF >> 16;
|
|
if (dwTextureFormats == 0)
|
|
{
|
|
for (DWORD i=0; i < dwNumTexCoord; i++)
|
|
{
|
|
pdwTextureCoordOffset[i] = i << 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD dwOffset = 0;
|
|
for (DWORD i=0; i < dwNumTexCoord; i++)
|
|
{
|
|
pdwTextureCoordOffset[i] = dwOffset;
|
|
dwOffset += g_TextureSize[dwTextureFormats & 3];
|
|
dwTextureFormats >>= 2;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Returns 2 bits of FVF texture format for the texture index
|
|
//
|
|
static inline DWORD FVFGetTextureFormat(DWORD dwFVF, DWORD dwTextureIndex)
|
|
{
|
|
return (dwFVF >> (dwTextureIndex*2 + 16)) & 3;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
// Returns texture format bits shifted to the right place
|
|
//
|
|
static inline DWORD FVFMakeTextureFormat(DWORD dwNumberOfCoordinates, DWORD dwTextureIndex)
|
|
{
|
|
return g_dwTextureFormat[dwNumberOfCoordinates] << ((dwTextureIndex << 1) + 16);
|
|
}
|
|
//---------------------------------------------------------------------
|
|
inline DWORD GetOutTexCoordSize(DWORD *pdwStage, DWORD dwInpTexCoordSize)
|
|
{
|
|
// Low byte has texture coordinate count
|
|
const DWORD dwTextureTransformFlags = pdwStage[D3DTSS_TEXTURETRANSFORMFLAGS] & 0xFF;
|
|
if (dwTextureTransformFlags == 0)
|
|
return dwInpTexCoordSize;
|
|
else
|
|
return (dwTextureTransformFlags << 2);
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// pDevI->nOutTexCoord should be initialized to the number of input texture coord sets
|
|
//
|
|
HRESULT EvalTextureTransforms(LPDIRECT3DDEVICEI pDevI, DWORD dwTexTransform,
|
|
DWORD *pdwOutTextureSize, DWORD *pdwOutTextureFormat)
|
|
{
|
|
DWORD dwOutTextureSize = 0; // Used to compute output vertex size
|
|
DWORD dwOutTextureFormat = 0; // Used to compute output texture FVF
|
|
// The bits are used to find out how the texture coordinates are used.
|
|
const DWORD __USED_BY_TRANSFORM = 1;
|
|
const DWORD __USED = 2;
|
|
// The low 16 bits are for _USED bits. The high 16 bits will hold
|
|
// re-mapped texture index for a stage
|
|
DWORD dwTexCoordUsage[D3DDP_MAXTEXCOORD];
|
|
memset(dwTexCoordUsage, 0, sizeof(dwTexCoordUsage));
|
|
|
|
// Re-mapping buffer will contain only stages that use texture
|
|
// This variable is used to count them
|
|
pDevI->dwNumTextureStages = 0;
|
|
DWORD dwNewIndex = 0; // Used to generate output index
|
|
// We need offsets for every input texture coordinate, because
|
|
// we could access them in random order.
|
|
// Offsets are not needed for strided input
|
|
DWORD dwTextureCoordOffset[D3DDP_MAXTEXCOORD];
|
|
if (!(pDevI->dwDeviceFlags & D3DDEV_STRIDE))
|
|
{
|
|
ComputeInpTexCoordOffsets(pDevI->nTexCoord, pDevI->dwVIDIn, dwTextureCoordOffset);
|
|
}
|
|
DWORD dwOutTextureCoordSize[D3DDP_MAXTEXCOORD];
|
|
// Go through all texture stages and find those which use texture coordinates
|
|
for (DWORD i=0; i < D3DDP_MAXTEXCOORD; i++)
|
|
{
|
|
if (pDevI->tsstates[i][D3DTSS_COLOROP] == D3DTOP_DISABLE)
|
|
break;
|
|
|
|
DWORD dwIndex = pDevI->tsstates[i][D3DTSS_TEXCOORDINDEX];
|
|
DWORD dwInpTextureFormat;
|
|
DWORD dwInpTexSize;
|
|
DWORD dwMapArrayIndex = pDevI->dwNumTextureStages;
|
|
LPD3DFE_TEXTURESTAGE pStage = &pDevI->textureStage[dwMapArrayIndex];
|
|
DWORD dwTexGenMode = dwIndex & ~0xFFFF;
|
|
dwIndex = dwIndex & 0xFFFF; // Remove texture generation mode
|
|
if (dwTexGenMode == D3DTSS_TCI_CAMERASPACENORMAL ||
|
|
dwTexGenMode == D3DTSS_TCI_CAMERASPACEPOSITION ||
|
|
dwTexGenMode == D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR)
|
|
{
|
|
dwInpTextureFormat = D3DFVF_TEXCOORDSIZE3(dwIndex);
|
|
dwInpTexSize = 3*sizeof(D3DVALUE);
|
|
pDevI->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
|
|
if (dwTexGenMode == D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR)
|
|
pDevI->dwDeviceFlags |= D3DDEV_NORMALINCAMERASPACE | D3DDEV_POSITIONINCAMERASPACE;
|
|
else
|
|
if (dwTexGenMode == D3DTSS_TCI_CAMERASPACENORMAL)
|
|
pDevI->dwDeviceFlags |= D3DDEV_NORMALINCAMERASPACE;
|
|
else
|
|
if (dwTexGenMode == D3DTSS_TCI_CAMERASPACEPOSITION)
|
|
pDevI->dwDeviceFlags |= D3DDEV_POSITIONINCAMERASPACE;
|
|
}
|
|
else
|
|
{
|
|
if (dwIndex >= pDevI->nTexCoord)
|
|
{
|
|
D3D_ERR("Texture index in a stage is greater than number of input texture coordinates");
|
|
return DDERR_GENERIC;
|
|
}
|
|
dwInpTextureFormat = FVFGetTextureFormat(pDevI->dwVIDIn, dwIndex);
|
|
dwInpTexSize = pDevI->dwTextureCoordSize[dwIndex];
|
|
pStage->dwInpOffset = dwTextureCoordOffset[dwIndex];
|
|
}
|
|
pStage->dwInpCoordIndex = dwIndex;
|
|
pStage->dwTexGenMode = dwTexGenMode;
|
|
pStage->dwOrgStage = i;
|
|
DWORD dwOutTexCoordSize; // Size of the texture coord set in bytes for this stage
|
|
if (dwTexTransform & 1)
|
|
{
|
|
pDevI->dwDeviceFlags |= D3DDEV_TEXTURETRANSFORM;
|
|
pStage->pmTextureTransform = &pDevI->mTexture[i];
|
|
dwOutTexCoordSize = GetOutTexCoordSize((DWORD*)&pDevI->tsstates[i], dwInpTexSize);
|
|
// If we have to add or remove some coordinates we go through
|
|
// the re-mapping path
|
|
if (dwOutTexCoordSize != dwInpTexSize)
|
|
pDevI->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
|
|
}
|
|
else
|
|
{
|
|
pStage->pmTextureTransform = NULL;
|
|
dwOutTexCoordSize = dwInpTexSize;
|
|
}
|
|
pStage->dwTexTransformFuncIndex = MakeTexTransformFuncIndex
|
|
(dwInpTexSize >> 2, dwOutTexCoordSize >> 2);
|
|
if ((dwTexCoordUsage[dwIndex] & 0xFFFF) == 0)
|
|
{
|
|
// Texture coordinate set is used first time
|
|
if (dwTexTransform & 1)
|
|
dwTexCoordUsage[dwIndex] |= __USED_BY_TRANSFORM;
|
|
dwTexCoordUsage[dwIndex] |= __USED;
|
|
}
|
|
else
|
|
{
|
|
// Texture coordinate set is used second or more time
|
|
if (dwTexTransform & 1)
|
|
{
|
|
// This set is used by two texture transforms or a
|
|
// texture transform and without it, so we have to
|
|
// generate an additional output texture coordinate
|
|
dwTexCoordUsage[dwIndex] |= __USED_BY_TRANSFORM;
|
|
pDevI->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
|
|
}
|
|
else
|
|
{
|
|
if (dwTexCoordUsage[dwIndex] & __USED_BY_TRANSFORM)
|
|
{
|
|
// This set is used by two texture transforms or a
|
|
// texture transform and without it, so we have to
|
|
// generate an additional output texture coordinate
|
|
pDevI->dwDeviceFlags |= D3DDEV_REMAPTEXTUREINDICES;
|
|
}
|
|
else
|
|
if (dwTexGenMode == 0)
|
|
{
|
|
// We do not have to generate new texture coord for this,
|
|
// we can re-use the same input texture coordinate
|
|
DWORD dwOutIndex = dwTexCoordUsage[dwIndex] >> 16;
|
|
pStage->dwOutCoordIndex = dwOutIndex;
|
|
goto l_NoNewOutTexCoord;
|
|
}
|
|
}
|
|
}
|
|
// If we are here, we have to generate new output texture coordinate set
|
|
pStage->dwOutCoordIndex = dwNewIndex;
|
|
dwTexCoordUsage[dwIndex] |= dwNewIndex << 16;
|
|
dwOutTextureSize += dwOutTexCoordSize;
|
|
dwOutTextureCoordSize[dwNewIndex] = dwOutTexCoordSize;
|
|
dwOutTextureFormat |= FVFMakeTextureFormat(dwOutTexCoordSize >> 2, dwNewIndex);
|
|
dwNewIndex++;
|
|
l_NoNewOutTexCoord:
|
|
pDevI->dwNumTextureStages++;
|
|
dwTexTransform >>= 1;
|
|
}
|
|
if (pDevI->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)
|
|
{
|
|
// Now, when we have to do re-mapping, we have to set new output texture
|
|
// coordinate set sizes
|
|
for (DWORD i=0; i < pDevI->dwNumTextureStages; i++)
|
|
{
|
|
pDevI->dwTextureCoordSize[i] = dwOutTextureCoordSize[i];
|
|
}
|
|
pDevI->nOutTexCoord = dwNewIndex;
|
|
}
|
|
*pdwOutTextureSize = dwOutTextureSize;
|
|
*pdwOutTextureFormat = dwOutTextureFormat;
|
|
return D3D_OK;
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// Sets texture transform pointer for every input texture coordinate set
|
|
//
|
|
void SetupTextureTransforms(LPDIRECT3DDEVICEI pDevI)
|
|
{
|
|
// Set texture transforms to NULL in case when some texture coordinates
|
|
// are not used by texture stages
|
|
memset(pDevI->pmTexture, 0, sizeof(pDevI->pmTexture));
|
|
|
|
for (DWORD i=0; i < pDevI->dwNumTextureStages; i++)
|
|
{
|
|
LPD3DFE_TEXTURESTAGE pStage = &pDevI->textureStage[i];
|
|
pDevI->pmTexture[pStage->dwInpCoordIndex] = pStage->pmTextureTransform;
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------
|
|
HRESULT CDirect3DDeviceIDP2::SetupFVFData(DWORD *pdwInpVertexSize)
|
|
{
|
|
if (this->dwDeviceFlags & D3DDEV_FVF)
|
|
return DIRECT3DDEVICEI::SetupFVFDataCommon(pdwInpVertexSize);
|
|
else
|
|
return DIRECT3DDEVICEI::SetupFVFData(pdwInpVertexSize);
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// Computes the following device data
|
|
// - dwVIDOut, based on input FVF id and device settings
|
|
// - nTexCoord
|
|
// - dwTextureCoordSizeTotal
|
|
// - dwTextureCoordSize[] array, based on the input FVF id
|
|
// - dwOutputSize, based on the output FVF id
|
|
//
|
|
// The function is called from ProcessVertices and DrawPrimitives code paths
|
|
//
|
|
// The following variables should be set in the pDevI:
|
|
// - dwVIDIn
|
|
//
|
|
// Number of texture coordinates is set based on dwVIDIn. ValidateFVF should
|
|
// make sure that it is not greater than supported by the driver
|
|
// Last settings for dwVIDOut and dwVIDIn are saved to speed up processing
|
|
//
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirect3DDeviceIDP2::SetupFVFData"
|
|
|
|
HRESULT DIRECT3DDEVICEI::SetupFVFDataCommon(DWORD *pdwInpVertexSize)
|
|
{
|
|
HRESULT ret;
|
|
this->dwFEFlags &= ~D3DFE_FVF_DIRTY;
|
|
// We have to restore texture stage indices if previous primitive
|
|
// re-mapped them
|
|
if (this->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)
|
|
{
|
|
RestoreTextureStages(this);
|
|
}
|
|
|
|
// Compute number of the input texture coordinates
|
|
|
|
this->nTexCoord = FVF_TEXCOORD_NUMBER(this->dwVIDIn);
|
|
|
|
// Compute size of input texture coordinates
|
|
|
|
this->dwTextureCoordSizeTotal = ComputeTextureCoordSize(this->dwVIDIn, this->dwInpTextureCoordSize);
|
|
|
|
// This size is the same for input and output FVFs in case when we do not have to
|
|
// expand number of texture coordinates
|
|
for (DWORD i=0; i < this->nTexCoord; i++)
|
|
this->dwTextureCoordSize[i] = this->dwInpTextureCoordSize[i];
|
|
|
|
if (pdwInpVertexSize)
|
|
{
|
|
*pdwInpVertexSize = GetVertexSizeFVF(this->dwVIDIn) + this->dwTextureCoordSizeTotal;
|
|
}
|
|
|
|
this->nOutTexCoord = this->nTexCoord;
|
|
|
|
if (FVF_TRANSFORMED(this->dwVIDIn))
|
|
{
|
|
// Set up vertex pointers
|
|
this->dwVIDOut = this->dwVIDIn;
|
|
ComputeOutputVertexOffsets(this);
|
|
return D3D_OK;
|
|
}
|
|
|
|
// Compute output FVF
|
|
|
|
this->dwVIDOut = D3DFVF_XYZRHW;
|
|
if (this->dwDeviceFlags & D3DDEV_DONOTSTRIPELEMENTS)
|
|
{
|
|
this->dwVIDOut |= D3DFVF_DIFFUSE | D3DFVF_SPECULAR;
|
|
}
|
|
else
|
|
{
|
|
// If normal present we have to compute specular and duffuse
|
|
// Otherwise set these bits the same as input.
|
|
// Not that normal should not be present for XYZRHW position type
|
|
if (this->dwDeviceFlags & D3DDEV_LIGHTING)
|
|
this->dwVIDOut |= D3DFVF_DIFFUSE | D3DFVF_SPECULAR;
|
|
else
|
|
this->dwVIDOut |= this->dwVIDIn & (D3DFVF_DIFFUSE | D3DFVF_SPECULAR);
|
|
// Always set specular flag if fog is enabled
|
|
if (this->rstates[D3DRENDERSTATE_FOGENABLE])
|
|
this->dwVIDOut |= D3DFVF_SPECULAR;
|
|
else
|
|
// Clear specular flag if specular disabled and we do not have specular in the input
|
|
if (!this->rstates[D3DRENDERSTATE_SPECULARENABLE] && !(this->dwVIDIn & D3DFVF_SPECULAR))
|
|
this->dwVIDOut &= ~D3DFVF_SPECULAR;
|
|
}
|
|
|
|
// Compute output vertex size without texture
|
|
this->dwOutputSize = GetVertexSizeFVF(this->dwVIDOut);
|
|
|
|
// Compute number of the output texture coordinates
|
|
|
|
// Transform enable bits
|
|
DWORD dwTexTransform = this->dwFlags2 & __FLAGS2_TEXTRANSFORM;
|
|
|
|
this->dwDeviceFlags &= ~D3DDEV_TEXTURETRANSFORM;
|
|
// When texture transform is enabled or texture coordinates are taken from
|
|
// the vertex data, output texture coordinates could be generated. so we go
|
|
// and evaluate texture stages
|
|
if ((dwTexTransform && this->nTexCoord > 0) ||
|
|
this->dwFlags2 & __FLAGS2_TEXGEN)
|
|
{
|
|
DWORD dwOutTextureSize; // Used to compute output vertex size
|
|
DWORD dwOutTextureFormat; // Used to compute output texture FVF
|
|
// There are texture transforms.
|
|
// Now we find out if some of the texture coordinates are used two or more
|
|
// times and used by a texture transform. In this case we have expand number
|
|
// of output texture coordinates.
|
|
ret = EvalTextureTransforms(this, dwTexTransform, &dwOutTextureSize, &dwOutTextureFormat);
|
|
if (ret != D3D_OK)
|
|
return ret;
|
|
if (this->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)
|
|
{
|
|
// For ProcessVertices calls user should set texture stages and
|
|
// wrap modes himself
|
|
if (!(this->dwFlags & D3DPV_VBCALL))
|
|
{
|
|
// dwVIDIn is used to force re-compute FVF in the
|
|
// SetTextureStageState. so we save and restore it.
|
|
DWORD dwVIDInSaved = this->dwVIDIn;
|
|
// Re-map indices in the texture stages and wrap modes
|
|
DWORD dwOrgWrapModes[D3DDP_MAXTEXCOORD];
|
|
memcpy(dwOrgWrapModes, &this->rstates[D3DRENDERSTATE_WRAP0], sizeof(dwOrgWrapModes));
|
|
for (DWORD i=0; i < this->dwNumTextureStages; i++)
|
|
{
|
|
LPD3DFE_TEXTURESTAGE pStage = &this->textureStage[i];
|
|
DWORD dwOutIndex = pStage->dwOutCoordIndex;
|
|
DWORD dwInpIndex = pStage->dwInpCoordIndex;
|
|
if (dwOutIndex != dwInpIndex || pStage->dwTexGenMode)
|
|
{
|
|
DWORD dwState = D3DRENDERSTATE_WRAP0 + dwOutIndex;
|
|
pStage->dwOrgWrapMode = dwOrgWrapModes[dwOutIndex];
|
|
DWORD dwValue = dwOrgWrapModes[dwInpIndex];
|
|
// We do not call UpdateInternaState because it
|
|
// will call ForceRecomputeFVF and we do not want this.
|
|
this->rstates[dwState] = dwValue;
|
|
this->SetRenderStateI((D3DRENDERSTATETYPE)dwState, dwValue);
|
|
// We do not call UpdateInternalTextureStageState because it
|
|
// will call ForceRecomputeFVF and we do not want this.
|
|
this->SetTSSI(pStage->dwOrgStage, D3DTSS_TEXCOORDINDEX, dwOutIndex);
|
|
// We do not call UpdateInternalTextureStageState because it
|
|
// will call ForceRecomputeFVF and we do not want this.
|
|
// We set some invalid value to the internal array, because otherwise
|
|
// a new SetTextureStageState could be filtered as redundant
|
|
tsstates[pStage->dwOrgStage][D3DTSS_TEXCOORDINDEX] = 0xFFFFFFFF;
|
|
}
|
|
}
|
|
this->dwVIDIn = dwVIDInSaved;
|
|
}
|
|
this->dwVIDOut |= dwOutTextureFormat;
|
|
this->dwOutputSize += dwOutTextureSize;
|
|
this->dwTextureCoordSizeTotal = dwOutTextureSize;
|
|
}
|
|
else
|
|
{ // We do not do re-mapping but we have to make correspondence between
|
|
// texture sets and texture transforms
|
|
SetupTextureTransforms(this);
|
|
|
|
// Copy input texture formats
|
|
this->dwVIDOut |= this->dwVIDIn & 0xFFFF0000;
|
|
this->dwOutputSize += this->dwTextureCoordSizeTotal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Copy input texture formats
|
|
this->dwVIDOut |= this->dwVIDIn & 0xFFFF0000;
|
|
this->dwOutputSize += this->dwTextureCoordSizeTotal;
|
|
}
|
|
|
|
if (this->dwDeviceFlags & D3DDEV_DONOTSTRIPELEMENTS)
|
|
{
|
|
if (this->nOutTexCoord == 0 && !(this->dwFlags & D3DPV_VBCALL))
|
|
{
|
|
this->dwOutputSize += 2*sizeof(D3DVALUE);
|
|
this->dwTextureCoordSize[0] = 0;
|
|
this->dwVIDOut |= (1 << D3DFVF_TEXCOUNT_SHIFT);
|
|
}
|
|
}
|
|
// Set up number of output texture coordinates
|
|
this->dwVIDOut |= (this->nOutTexCoord << D3DFVF_TEXCOUNT_SHIFT);
|
|
if (this->dwVIDOut & 0xFFFF0000 && this->deviceType < D3DDEVTYPE_DX7HAL)
|
|
{
|
|
D3D_ERR("Texture format bits in the output FVF for this device should be 0");
|
|
return DDERR_INVALIDPARAMS;
|
|
}
|
|
|
|
// Set up vertex pointers
|
|
if (!(this->dwFlags & D3DPV_VBCALL))
|
|
UpdateGeometryLoopData(this);
|
|
|
|
// In case if COLORVERTEX is TRUE, the vertexAlpha could be overriden
|
|
// by vertex alpha
|
|
this->lighting.alpha = (DWORD)this->lighting.materialAlpha;
|
|
this->lighting.alphaSpecular = (DWORD)this->lighting.materialAlphaS;
|
|
|
|
this->dwFEFlags |= D3DFE_VERTEXBLEND_DIRTY;
|
|
|
|
return D3D_OK;
|
|
}
|
|
|
|
#if DBG
|
|
void CDirect3DDeviceIDP2::ValidateVertex(LPDWORD lpdwVertex)
|
|
{
|
|
if (FVF_TRANSFORMED(dp2data.dwVertexType))
|
|
{
|
|
float left, right, top, bottom;
|
|
if (dwDeviceFlags & D3DDEV_GUARDBAND)
|
|
{
|
|
left = lpD3DExtendedCaps->dvGuardBandLeft;
|
|
right = lpD3DExtendedCaps->dvGuardBandRight;
|
|
top = lpD3DExtendedCaps->dvGuardBandTop;
|
|
bottom = lpD3DExtendedCaps->dvGuardBandBottom;
|
|
}
|
|
else
|
|
{
|
|
left = (float)m_Viewport.dwX;
|
|
top = (float)m_Viewport.dwY;
|
|
right = (float)m_Viewport.dwX + m_Viewport.dwWidth;
|
|
bottom = (float)m_Viewport.dwY + m_Viewport.dwHeight;
|
|
}
|
|
if (*(float*)lpdwVertex < left || *(float*)lpdwVertex++ > right)
|
|
DPF_ERR("X coordinate out of range!");
|
|
if (*(float*)lpdwVertex < top || *(float*)lpdwVertex++ > bottom)
|
|
DPF_ERR("Y coordinate out of range!");
|
|
if (rstates[D3DRENDERSTATE_ZENABLE] ||
|
|
rstates[D3DRENDERSTATE_ZWRITEENABLE])
|
|
{
|
|
// Allow a little slack for those generating triangles exactly on the
|
|
// depth limit. Needed for Quake.
|
|
if (*(float*)lpdwVertex < -0.00015f || *(float*)lpdwVertex++ > 1.00015f)
|
|
DPF_ERR("Z coordinate out of range!");
|
|
}
|
|
if (FVF_TEXCOORD_NUMBER(dp2data.dwVertexType) > 0)
|
|
{
|
|
if (*(float*)lpdwVertex <= 0 )
|
|
{
|
|
DPF_ERR("RHW out of range!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDirect3DDeviceIDP2::ValidateCommand(LPD3DHAL_DP2COMMAND lpCmd)
|
|
{
|
|
DWORD dwTexCoordSizeDummy[8];
|
|
DWORD dwVertexSize = GetVertexSizeFVF(dp2data.dwVertexType) + ComputeTextureCoordSize(dp2data.dwVertexType, dwTexCoordSizeDummy);
|
|
WORD wStart, wCount;
|
|
switch (lpCmd->bCommand)
|
|
{
|
|
case D3DDP2OP_TRIANGLELIST:
|
|
{
|
|
LPD3DHAL_DP2TRIANGLELIST pTri = (LPD3DHAL_DP2TRIANGLELIST)(lpCmd + 1);
|
|
wStart = pTri->wVStart;
|
|
wCount =lpCmd->wPrimitiveCount * 3;
|
|
}
|
|
break;
|
|
case D3DDP2OP_TRIANGLESTRIP:
|
|
case D3DDP2OP_TRIANGLEFAN:
|
|
{
|
|
LPD3DHAL_DP2TRIANGLEFAN pFan = (LPD3DHAL_DP2TRIANGLEFAN)(lpCmd + 1);
|
|
wStart = pFan->wVStart;
|
|
wCount = lpCmd->wPrimitiveCount + 2;
|
|
}
|
|
break;
|
|
case D3DDP2OP_TRIANGLEFAN_IMM:
|
|
{
|
|
wCount = lpCmd->wPrimitiveCount + 2;
|
|
for (WORD i=0; i < wCount; ++i)
|
|
{
|
|
ValidateVertex((LPDWORD)((LPBYTE)(lpCmd + 1) + i * dwVertexSize));
|
|
}
|
|
}
|
|
// Fall through
|
|
default:
|
|
return;
|
|
}
|
|
for (WORD i = wStart; i < wStart + wCount; ++i)
|
|
{
|
|
if( dp2data.dwFlags & D3DHALDP2_USERMEMVERTICES )
|
|
ValidateVertex((LPDWORD)((LPBYTE)(dp2data.lpVertices) + i * dwVertexSize));
|
|
else
|
|
ValidateVertex((LPDWORD)((LPBYTE)(dp2data.lpDDVertex->lpGbl->fpVidMem) + i * dwVertexSize));
|
|
}
|
|
}
|
|
|
|
#endif
|