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