windows-nt/Source/XPSP1/NT/multimedia/directx/dxg/d3d8/fw/dxgcreat.cpp
2020-09-26 16:20:57 +08:00

2370 lines
71 KiB
C++

/*==========================================================================
*
* Copyright (C) 1995-1999 Microsoft Corporation. All Rights Reserved.
*
* File: dxgcreat.cpp
* Content Creates the dxg object
*
***************************************************************************/
#include "ddrawpr.h"
// Includes for creation stuff
#include "mipmap.hpp"
#include "mipvol.hpp"
#include "cubemap.hpp"
#include "surface.hpp"
#include "vbuffer.hpp"
#include "ibuffer.hpp"
#include "swapchan.hpp"
#include "resource.hpp"
#include "d3di.hpp"
#include "resource.inl"
#ifdef WINNT
extern "C" BOOL IsWhistler();
#endif
//---------------------------------------------------------------------------
// CBaseDevice methods
//---------------------------------------------------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::AddRef"
STDMETHODIMP_(ULONG) CBaseDevice::AddRef(void)
{
API_ENTER_NO_LOCK(this);
// InterlockedIncrement requires the memory
// to be aligned on DWORD boundary
DXGASSERT(((ULONG_PTR)(&m_cRef) & 3) == 0);
InterlockedIncrement((LONG *)&m_cRef);
return m_cRef;
} // AddRef
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::Release"
STDMETHODIMP_(ULONG) CBaseDevice::Release(void)
{
API_ENTER_NO_LOCK(this);
// InterlockedDecrement requires the memory
// to be aligned on DWORD boundary
DXGASSERT(((ULONG_PTR)(&m_cRef) & 3) == 0);
InterlockedDecrement((LONG *)&m_cRef);
if (m_cRef != 0)
return m_cRef;
// If we are about to release; we
// DPF a warning if the release is on a different
// thread than the create
if (!CheckThread())
{
DPF_ERR("Final Release for a device can only be called "
"from the thread that the "
"device was created from.");
// No failure can be returned; but this is
// dangerous situation for the app since
// windows messages may still be processed
}
delete this;
return 0;
} // Release
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::QueryInterface"
STDMETHODIMP CBaseDevice::QueryInterface(REFIID riid, LPVOID FAR *ppv)
{
API_ENTER(this);
if (!VALID_PTR_PTR(ppv))
{
DPF_ERR("Invalid pointer passed to QueryInterface for IDirect3DDevice8" );
return D3DERR_INVALIDCALL;
}
if (!VALID_PTR(&riid, sizeof(GUID)))
{
DPF_ERR("Invalid guid memory address to QueryInterface for IDirect3DDevice8");
return D3DERR_INVALIDCALL;
}
if (riid == IID_IUnknown || riid == IID_IDirect3DDevice8)
{
*ppv = static_cast<void*>(static_cast<IDirect3DDevice8*>(this));
AddRef();
}
else
{
DPF_ERR("Unsupported Interface identifier passed to QueryInterface for IDirect3DDevice8");
*ppv = NULL;
return E_NOINTERFACE;
}
return S_OK;
} // QueryInterface
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateAdditionalSwapChain"
// Swap Chain stuff
STDMETHODIMP
CBaseDevice::CreateAdditionalSwapChain(
D3DPRESENT_PARAMETERS *pPresentationParams,
IDirect3DSwapChain8 **pSwapChain)
{
API_ENTER(this);
if (!VALID_WRITEPTR(pPresentationParams, sizeof(D3DPRESENT_PARAMETERS)))
{
DPF_ERR("Invalid D3DPRESENT_PARAMETERS pointer to CreateAdditionalSwapChain");
return D3DERR_INVALIDCALL;
}
if (!VALID_PTR_PTR(pSwapChain))
{
DPF_ERR("Invalid IDirect3DSwapChain8* pointer to CreateAdditionalSwapChain");
return D3DERR_INVALIDCALL;
}
// Zero out return param
*pSwapChain = NULL;
if (NULL == m_pSwapChain)
{
DPF_ERR("No Swap Chain present; CreateAdditionalSwapChain fails");
return D3DERR_INVALIDCALL;
}
if (pPresentationParams->BackBufferFormat == D3DFMT_UNKNOWN)
{
DPF_ERR("Invalid backbuffer format specified. CreateAdditionalSwapChain fails");
return D3DERR_INVALIDCALL;
}
if (m_pSwapChain->m_PresentationData.Windowed
&& pPresentationParams->Windowed)
{
// both device and swapchain have to be windowed
HRESULT hr;
if ((NULL == pPresentationParams->hDeviceWindow)
&& (NULL == FocusWindow()))
{
DPF_ERR("Neither hDeviceWindow nor Focus window specified. CreateAdditionalSwapChain fails");
return D3DERR_INVALIDCALL;
}
*pSwapChain = new CSwapChain(
this,
REF_EXTERNAL);
if (*pSwapChain == NULL)
{
DPF_ERR("Out of memory creating swap chain. CreateAdditionalSwapChain fails");
return E_OUTOFMEMORY;
}
static_cast<CSwapChain *> (*pSwapChain) ->Init(
pPresentationParams,
&hr);
if (FAILED(hr))
{
DPF_ERR("Failure initializing swap chain. CreateAdditionalSwapChain fails");
(*pSwapChain)->Release();
*pSwapChain = NULL;
return hr;
}
return hr;
}
else
{
DPF_ERR("Can't Create Additional SwapChain for FullScreen");
return D3DERR_INVALIDCALL;
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::SetCursorProperties"
STDMETHODIMP
CBaseDevice::SetCursorProperties(
UINT xHotSpot,
UINT yHotSpot,
IDirect3DSurface8 *pCursorBitmap)
{
API_ENTER(this);
if (pCursorBitmap == NULL)
{
DPF_ERR("Invalid parameter for pCursorBitmap");
return D3DERR_INVALIDCALL;
}
CBaseSurface *pCursorSrc = static_cast<CBaseSurface*>(pCursorBitmap);
if (pCursorSrc->InternalGetDevice() != this)
{
DPF_ERR("Cursor Surface wasn't allocated with this Device. SetCursorProperties fails");
return D3DERR_INVALIDCALL;
}
if (SwapChain()->m_pCursor)
{
return SwapChain()->m_pCursor->SetProperties(
xHotSpot,
yHotSpot,
pCursorSrc);
}
else
{
DPF_ERR("Device is lost. SetCursorProperties does nothing.");
return S_OK;
}
} // SetCursorProperties
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::SetCursorPosition"
STDMETHODIMP_(void)
CBaseDevice::SetCursorPosition(
UINT xScreenSpace,
UINT yScreenSpace,
DWORD Flags)
{
API_ENTER_VOID(this);
if (SwapChain()->m_pCursor)
SwapChain()->m_pCursor->SetPosition(xScreenSpace,yScreenSpace,Flags);
else
DPF_ERR("Device is lost. SetCursorPosition does nothing.");
return;
} // SetCursorPosition
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::ShowCursor"
STDMETHODIMP_(BOOL)
CBaseDevice::ShowCursor(
BOOL bShow // cursor visibility flag
)
{
API_ENTER_RET(this, BOOL);
if (SwapChain()->m_pCursor)
return m_pSwapChain->m_pCursor->SetVisibility(bShow);
DPF_ERR("Device is lost. ShowCursor does nothing.");
return FALSE;
} // ShowCursor
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::Reset"
STDMETHODIMP
CBaseDevice::Reset(
D3DPRESENT_PARAMETERS *pPresentationParams
)
{
API_ENTER(this);
HRESULT hr;
if (!CheckThread())
{
DPF_ERR("Reset can only be called from the thread that the "
"device was created from.");
return D3DERR_INVALIDCALL;
}
if (!VALID_WRITEPTR(pPresentationParams, sizeof(D3DPRESENT_PARAMETERS)))
{
DPF_ERR("Invalid D3DPRESENT_PARAMETERS pointer, Reset fails");
hr = D3DERR_INVALIDCALL;
goto LoseDevice;
}
if (NULL == FocusWindow())
{
if (!pPresentationParams->Windowed)
{
DPF_ERR("Can't Reset a Device w/o Focus window to Fullscreen");
hr = D3DERR_INVALIDCALL;
goto LoseDevice;
}
else
if (NULL == pPresentationParams->hDeviceWindow)
{
DPF_ERR("Neither hDeviceWindow nor Focus window specified. Reset fails.");
hr = D3DERR_INVALIDCALL;
goto LoseDevice;
}
}
if (pPresentationParams->BackBufferFormat == D3DFMT_UNKNOWN)
{
DPF_ERR("Invalid backbuffer format specified. Reset fails");
hr = D3DERR_INVALIDCALL;
goto LoseDevice;
}
if (NULL == m_pSwapChain)
{
DPF_ERR("No Swap Chain present, Reset fails");
hr = D3DERR_INVALIDCALL;
goto LoseDevice;
}
hr = TestCooperativeLevel();
if (D3DERR_DEVICELOST == hr)
{
DPF_ERR("Reset fails. D3DERR_DEVICELOST returned.");
goto LoseDevice;
}
else if (D3DERR_DEVICENOTRESET == hr)
{
// There might be a external mode switch or ALT-TAB from fullscreen
FetchDirectDrawData(GetDeviceData(), GetInitFunction(),
Enum()->GetUnknown16(AdapterIndex()),
Enum()->GetHalOpList(AdapterIndex()),
Enum()->GetNumHalOps(AdapterIndex()));
// only update the DesktopMode
// if lost device was windowed or Fullscreen(but ALT-TABed away)
// in Multimon case, even Fullscreen with exclusive mode Device could
// be lost due to a mode change in other adapters and DesktopMode
// should NOT be updated as it's the current fullscreen mode
if (!SwapChain()->m_bExclusiveMode)
{
m_DesktopMode.Height = DisplayHeight();
m_DesktopMode.Width = DisplayWidth();
m_DesktopMode.Format = DisplayFormat();
m_DesktopMode.RefreshRate = DisplayRate();
}
}
else if (m_fullscreen)
{
SwapChain()->FlipToGDISurface();
}
if ( S_OK == hr && RenderTarget())
{
RenderTarget()->Sync();
}
static_cast<CD3DBase*>(this)->CleanupTextures();
hr = m_pSwapChain->Reset(
pPresentationParams);
if (FAILED(hr))
{
goto LoseDevice;
}
if (pPresentationParams->EnableAutoDepthStencil)
{
// Need to validate that this Z-buffer matches
// the HW
hr = CheckDepthStencilMatch(pPresentationParams->BackBufferFormat,
pPresentationParams->AutoDepthStencilFormat);
if (FAILED(hr))
{
DPF_ERR("AutoDepthStencilFormat does not match BackBufferFormat "
"because the current Device requires the bitdepth of the "
"zbuffer to match the render-target. Reset Failed");
goto LoseDevice;
}
IDirect3DSurface8 *pSurf;
hr = CSurface::CreateZStencil(this,
m_pSwapChain->Width(),
m_pSwapChain->Height(),
pPresentationParams->AutoDepthStencilFormat,
pPresentationParams->MultiSampleType,
REF_INTRINSIC,
&pSurf);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create automatic zstencil surface. Reset Fails");
goto LoseDevice;
}
DXGASSERT(m_pAutoZStencil == NULL);
m_pAutoZStencil = static_cast<CBaseSurface *>(pSurf);
}
// Disconnect Buffers from our device's state if there is any
// I tried to not Destroy() upon window->window Reset
// however, there are many other cares which require it,
// such as device lost or m_pDDI=NULL due to earlier failure
// also SetRenderTarget() is tough when m_pDDI is bad
// some driver(like ATI Rage3) could not Reset view correctly
// even after SetRenderTarget()
// therefore always Destroy and do a Init, as a result, driver
// will always get a DestroyContext and CreateContext clean
// static_cast<CD3DBase*>(this)->Destroy();
UpdateRenderTarget(m_pSwapChain->m_ppBackBuffers[0], m_pAutoZStencil);
hr = static_cast<CD3DBase*>(this)->Init();
LoseDevice:
if (FAILED(hr))
{
DPF_ERR("Reset failed and Reset/TestCooperativeLevel/Release "
"are the only legal APIs to be called subsequently");
if ((SwapChain()) && (!SwapChain()->m_PresentationData.Windowed))
{
// release the exclusive upon failure
SwapChain()->m_PresentationData.Windowed = TRUE;
SwapChain()->SetCooperativeLevel();
}
D3D8LoseDevice(GetHandle());
}
else
{
hr = CResource::RestoreDriverManagementState(this);
if (FAILED(hr))
{
goto LoseDevice;
}
hr = static_cast<CD3DBase*>(this)->ResetShaders();
if (FAILED(hr))
{
goto LoseDevice;
}
}
m_fullscreen = !SwapChain()->m_PresentationData.Windowed;
return hr;
} // Reset
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::SetGammaRamp"
STDMETHODIMP_(void)
CBaseDevice::SetGammaRamp(DWORD dwFlags, CONST D3DGAMMARAMP *pRamp)
{
API_ENTER_VOID(this);
if (NULL == pRamp)
{
DPF_ERR("Invalid D3DGAMMARAMP pointer. SetGammaRamp ignored.");
return;
}
if (m_pSwapChain == NULL)
{
DPF_ERR("No Swap Chain present; SetGammaRamp fails");
return;
}
m_pSwapChain->SetGammaRamp(dwFlags, pRamp);
} // SetGammaRamp
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetGammaRamp"
STDMETHODIMP_(void)
CBaseDevice::GetGammaRamp(D3DGAMMARAMP *pRamp)
{
API_ENTER_VOID(this);
if (NULL == pRamp)
{
DPF_ERR("Invalid D3DGAMMARAMP pointer. GetGammaRamp ignored");
return;
}
if (m_pSwapChain == NULL)
{
DPF_ERR("No Swap Chain present; GetGammaRamp fails");
return;
}
m_pSwapChain->GetGammaRamp(pRamp);
} // GetGammaRamp
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetBackBuffer"
HRESULT
CBaseDevice::GetBackBuffer(UINT iBackBuffer,
D3DBACKBUFFER_TYPE Type,
IDirect3DSurface8 **ppBackBuffer)
{
API_ENTER(this);
if (!VALID_PTR_PTR(ppBackBuffer))
{
DPF_ERR("Invalid IDirect3DSurface8* pointer to GetBackBuffer");
return D3DERR_INVALIDCALL;
}
// Zero out return param
*ppBackBuffer = NULL;
if (m_pSwapChain == NULL)
{
DPF_ERR("No Swap Chain present; GetBackBuffer fails");
return D3DERR_INVALIDCALL;
}
return m_pSwapChain->GetBackBuffer(iBackBuffer, Type, ppBackBuffer);
} // GetBackBuffer
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::Present"
STDMETHODIMP
CBaseDevice::Present(
CONST RECT *pSrcRect,
CONST RECT *pDestRect,
HWND hWndDestOverride,
CONST RGNDATA *pDstRegion
)
{
API_ENTER(this);
if (m_pSwapChain == NULL)
{
DPF_ERR("No Swap Chain present; Present fails");
return D3DERR_INVALIDCALL;
}
return m_pSwapChain->Present(pSrcRect, pDestRect, hWndDestOverride, pDstRegion);
} // Present
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::TestCooperativeLevel"
STDMETHODIMP CBaseDevice::TestCooperativeLevel(void)
{
API_ENTER(this);
if (D3D8IsDeviceLost(GetHandle()))
{
#ifdef WINNT
if (m_pSwapChain)
{
BOOL bDeactivated = m_pSwapChain->IsWinProcDeactivated();
if (bDeactivated)
return D3DERR_DEVICELOST;
}
HWND EnumFocusWindow = Enum()->ExclusiveOwnerWindow();
if (EnumFocusWindow &&
EnumFocusWindow != FocusWindow())
{
DPF(0, "Another device in the same process has gone full-screen."
" If you wanted both to go full-screen at the same time,"
" you need to pass the same HWND for the Focus Window.");
return D3DERR_DEVICELOST;
}
BOOL bThisDeviceOwnsExclusive;
BOOL bExclusiveExists = Enum()->CheckExclusiveMode(this,
&bThisDeviceOwnsExclusive, FALSE);
if (bExclusiveExists && !bThisDeviceOwnsExclusive)
{
return D3DERR_DEVICELOST;
}
#endif //WINNT
if (D3D8CanRestoreNow(GetHandle()))
{
return D3DERR_DEVICENOTRESET;
}
return D3DERR_DEVICELOST;
}
return S_OK;
} // TestCooperativeLevel
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetRasterStatus"
STDMETHODIMP CBaseDevice::GetRasterStatus(D3DRASTER_STATUS *pStatus)
{
API_ENTER(this);
if (!VALID_WRITEPTR(pStatus, sizeof(*pStatus)))
{
DPF_ERR("Invalid Raster Status parameter to GetRasterStatus");
return D3DERR_INVALIDCALL;
}
if (!(GetD3DCaps()->Caps & D3DCAPS_READ_SCANLINE))
{
pStatus->ScanLine = 0;
pStatus->InVBlank = FALSE;
DPF_ERR("Current device doesn't support D3DCAPS_READ_SCANLINE functionality. GetRasterStatus fails.");
return D3DERR_INVALIDCALL;
}
D3D8_GETSCANLINEDATA getScanLineData;
getScanLineData.hDD = GetHandle();
DWORD dwRet = GetHalCallbacks()->GetScanLine(&getScanLineData);
if (dwRet == DDHAL_DRIVER_HANDLED)
{
if (getScanLineData.ddRVal == S_OK)
{
pStatus->InVBlank = getScanLineData.bInVerticalBlank;
if (getScanLineData.bInVerticalBlank)
{
pStatus->ScanLine = 0;
}
else
{
pStatus->ScanLine = getScanLineData.dwScanLine;
}
}
else
{
DPF_ERR("Device failed GetScanline. GetRasterStatus fails");
pStatus->ScanLine = 0;
pStatus->InVBlank = FALSE;
return D3DERR_NOTAVAILABLE;
}
}
else
{
DPF_ERR("Device failed GetScanline. GetRasterStatus fails.");
pStatus->ScanLine = 0;
pStatus->InVBlank = FALSE;
return D3DERR_NOTAVAILABLE;
}
return S_OK;
} // GetRasterStatus
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetDirect3D"
STDMETHODIMP CBaseDevice::GetDirect3D(LPDIRECT3D8 *pD3D8)
{
API_ENTER(this);
if (pD3D8 == NULL)
{
DPF_ERR("Invalid pointer specified. GetDirect3D fails.");
return D3DERR_INVALIDCALL;
}
DXGASSERT(m_pD3DClass);
m_pD3DClass->AddRef();
*pD3D8 = m_pD3DClass;
return D3D_OK;
} // GetDirect3D
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetCreationParameters"
STDMETHODIMP CBaseDevice::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS *pParameters)
{
API_ENTER(this);
if (!VALID_WRITEPTR(pParameters, sizeof(D3DDEVICE_CREATION_PARAMETERS)))
{
DPF_ERR("bad pointer for pParameters passed to GetCreationParameters");
return D3DERR_INVALIDCALL;
}
pParameters->AdapterOrdinal = m_AdapterIndex;
pParameters->DeviceType = m_DeviceType;
pParameters->BehaviorFlags = m_dwOriginalBehaviorFlags;
pParameters->hFocusWindow = m_hwndFocusWindow;
return S_OK;
} // GetCreationParameters
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetDisplayMode"
STDMETHODIMP CBaseDevice::GetDisplayMode(D3DDISPLAYMODE *pMode)
{
API_ENTER(this);
if (!VALID_WRITEPTR(pMode, sizeof(*pMode)))
{
DPF_ERR("Invalid pointer specified to GetDisplayMode");
return D3DERR_INVALIDCALL;
}
pMode->Width = DisplayWidth();
pMode->Height = DisplayHeight();
pMode->Format = DisplayFormat();
pMode->RefreshRate = DisplayRate();
return D3D_OK;
} // GetDisplayMode
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetAvailableTextureMem"
STDMETHODIMP_(UINT) CBaseDevice::GetAvailableTextureMem(void)
{
API_ENTER_RET(this, UINT);
D3D8_GETAVAILDRIVERMEMORYDATA GetAvailDriverMemory;
GetAvailDriverMemory.hDD = GetHandle();
GetAvailDriverMemory.Pool = D3DPOOL_DEFAULT;
GetAvailDriverMemory.dwUsage = D3DUSAGE_TEXTURE;
GetAvailDriverMemory.dwFree = 0;
GetHalCallbacks()->GetAvailDriverMemory(&GetAvailDriverMemory);
#define ONE_MEG_O_VRAM 0x100000
//Round to nearest meg:
return (GetAvailDriverMemory.dwFree + ONE_MEG_O_VRAM/2) & (~(ONE_MEG_O_VRAM-1));
} // GetAvailableTextureMem
#undef DPF_MODNAME
#define DPF_MODNAME "CanHardwareBlt"
BOOL CanHardwareBlt (const D3D8_DRIVERCAPS* pDriverCaps,
D3DPOOL SrcPool,
D3DFORMAT SrcFormat,
D3DPOOL DstPool,
D3DFORMAT DstFormat,
D3DDEVTYPE DeviceType)
{
// Pools are supposed to be real pools as opposed to
// what the app specified
DXGASSERT(SrcPool != D3DPOOL_DEFAULT);
DXGASSERT(DstPool != D3DPOOL_DEFAULT);
DXGASSERT(VALID_INTERNAL_POOL(SrcPool));
DXGASSERT(VALID_INTERNAL_POOL(DstPool));
//Driver should never be allowed to see scratch:
if (SrcPool == D3DPOOL_SCRATCH ||
DstPool == D3DPOOL_SCRATCH)
{
return FALSE;
}
// For this case, we want to just lock and memcpy. Why?
// It's a software driver, so it's going to be a memcpy anyway,
// and we special case blt since we want to use a real hardware
// blt for Present even when running a software driver. So either
// we lock and memcpy, or we have to keep track of two different
// Blt entry points (one for the real driver and one for the software
// driver) just so the software driver can do the memcpy itself.
if (DeviceType != D3DDEVTYPE_HAL)
{
return FALSE;
}
// Check that source and dest formats match
DXGASSERT(SrcFormat == DstFormat);
// FourCC may not be copy-able
if (CPixel::IsFourCC(SrcFormat))
{
if (!(pDriverCaps->D3DCaps.Caps2 & DDCAPS2_COPYFOURCC))
{
return FALSE;
}
}
// We can't do HW blts if either source or
// dest is in system memory and the driver
// needs PageLocks
if (SrcPool == D3DPOOL_SYSTEMMEM ||
DstPool == D3DPOOL_SYSTEMMEM)
{
if (!(pDriverCaps->D3DCaps.Caps2 & DDCAPS2_NOPAGELOCKREQUIRED))
{
return FALSE;
}
// Now this is tricky; but in DX7 we checked this cap when
// deciding whether to do BLTs involving system-memory but not
// when we decided whether to do real Blts. We need to check this.
if (!(pDriverCaps->D3DCaps.Caps & DDCAPS_CANBLTSYSMEM))
{
return FALSE;
}
}
// Check AGP caps first
if (pDriverCaps->D3DCaps.Caps2 & DDCAPS2_NONLOCALVIDMEMCAPS)
{
if (SrcPool == D3DPOOL_SYSTEMMEM)
{
if ((DstPool == D3DPOOL_NONLOCALVIDMEM) &&
(pDriverCaps->D3DCaps.Caps2 & DDCAPS2_SYSTONONLOCAL_AS_SYSTOLOCAL) &&
(pDriverCaps->SVBCaps & DDCAPS_BLT))
{
return TRUE;
}
else if (((DstPool == D3DPOOL_LOCALVIDMEM) ||
(DstPool == D3DPOOL_MANAGED)) &&
(pDriverCaps->SVBCaps & DDCAPS_BLT))
{
return TRUE;
}
}
else if (SrcPool == D3DPOOL_NONLOCALVIDMEM)
{
if (((DstPool == D3DPOOL_LOCALVIDMEM) ||
(DstPool == D3DPOOL_MANAGED)) &&
(pDriverCaps->NLVCaps & DDCAPS_BLT))
{
return TRUE;
}
}
else if ((SrcPool == D3DPOOL_LOCALVIDMEM) ||
(SrcPool == D3DPOOL_MANAGED))
{
if (((DstPool == D3DPOOL_LOCALVIDMEM) ||
(DstPool == D3DPOOL_MANAGED)) &&
(pDriverCaps->D3DCaps.Caps & DDCAPS_BLT))
{
return TRUE;
}
else if ((DstPool == D3DPOOL_SYSTEMMEM) &&
(pDriverCaps->VSBCaps & DDCAPS_BLT))
{
return TRUE;
}
}
}
else
{
if (SrcPool == D3DPOOL_SYSTEMMEM)
{
if (((DstPool == D3DPOOL_LOCALVIDMEM) ||
(DstPool == D3DPOOL_MANAGED)) &&
(pDriverCaps->SVBCaps & DDCAPS_BLT))
{
return TRUE;
}
}
else if ((SrcPool == D3DPOOL_LOCALVIDMEM) ||
(SrcPool == D3DPOOL_MANAGED))
{
if (((DstPool == D3DPOOL_LOCALVIDMEM) ||
(DstPool == D3DPOOL_MANAGED)) &&
(pDriverCaps->D3DCaps.Caps & DDCAPS_BLT))
{
return TRUE;
}
else if ((DstPool == D3DPOOL_SYSTEMMEM) &&
(pDriverCaps->VSBCaps & DDCAPS_BLT))
{
return TRUE;
}
}
}
return FALSE;
} // CanHardwareBlt
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CopyRects"
STDMETHODIMP CBaseDevice::CopyRects(IDirect3DSurface8 *pSrcSurface,
CONST RECT *pSrcRectsArray,
UINT cRects,
IDirect3DSurface8 *pDstSurface,
CONST POINT *pDstPointsArray)
{
API_ENTER(this);
D3DSURFACE_DESC SrcDesc;
D3DSURFACE_DESC DstDesc;
HRESULT hr;
UINT i;
// Do some basic paramater checking
if (!VALID_PTR(pSrcSurface, sizeof(void*)) ||
!VALID_PTR(pDstSurface, sizeof(void*)))
{
DPF_ERR("NULL surface interface specified. CopyRect fails");
return D3DERR_INVALIDCALL;
}
CBaseSurface *pSrc = static_cast<CBaseSurface*>(pSrcSurface);
if (pSrc->InternalGetDevice() != this)
{
DPF_ERR("SrcSurface was not allocated with this Device. CopyRect fails.");
return D3DERR_INVALIDCALL;
}
CBaseSurface *pDst = static_cast<CBaseSurface*>(pDstSurface);
if (pDst->InternalGetDevice() != this)
{
DPF_ERR("DstSurface was not allocated with this Device. CopyRect fails.");
return D3DERR_INVALIDCALL;
}
hr = pSrc->GetDesc(&SrcDesc);
DXGASSERT(SUCCEEDED(hr));
hr = pDst->GetDesc(&DstDesc);
DXGASSERT(SUCCEEDED(hr));
// Source can not be a load-once surface
if (SrcDesc.Usage & D3DUSAGE_LOADONCE)
{
DPF_ERR("CopyRects can not be used from a Load_Once surface");
return D3DERR_INVALIDCALL;
}
// Destination can not be a load-once surface
// if it isn't currently lockable.
if (DstDesc.Usage & D3DUSAGE_LOADONCE)
{
if (pDst->IsLoaded())
{
DPF_ERR("Destination for CopyRects a Load_Once surface that has"
" already been loaded. CopyRects failed.");
return D3DERR_INVALIDCALL;
}
}
// Source can not be already locked
if (pSrc->IsLocked())
{
DPF_ERR("Source for CopyRects is already Locked. CopyRect failed.");
return D3DERR_INVALIDCALL;
}
if (pDst->IsLocked())
{
DPF_ERR("Destination for CopyRects is already Locked. CopyRect failed.");
return D3DERR_INVALIDCALL;
}
if (SrcDesc.Format != DstDesc.Format)
{
DPF_ERR("Source and dest surfaces are different formats. CopyRects fails");
return D3DERR_INVALIDCALL;
}
if (CPixel::IsEnumeratableZ(SrcDesc.Format) &&
!CPixel::IsIHVFormat(SrcDesc.Format))
{
DPF_ERR("CopyRects is not supported for Z formats.");
return D3DERR_INVALIDCALL;
}
// Make sure that the rects are entirely within the surface
if ((cRects > 0) && (pSrcRectsArray == NULL))
{
DPF_ERR("Number of rects > 0, but rect array is NULL. CopyRects fails.");
return D3DERR_INVALIDCALL;
}
D3DFORMAT InternalFormat = pSrc->InternalGetDesc().Format;
BOOL bDXT = CPixel::IsDXT(InternalFormat);
for (i = 0; i < cRects; i++)
{
if (!CPixel::IsValidRect(InternalFormat,
SrcDesc.Width,
SrcDesc.Height,
&pSrcRectsArray[i]))
{
DPF_ERR("CopyRects failed");
return D3DERR_INVALIDCALL;
}
// Validate the point parameter;
// if it is NULL, then it means that we're
// to use the left/top that was in the corresponding rect.
CONST POINT *pPoint;
if (pDstPointsArray != NULL)
{
pPoint = &pDstPointsArray[i];
}
else
{
pPoint = (CONST POINT *)&pSrcRectsArray[i];
}
if (bDXT)
{
if ((pPoint->x & 3) ||
(pPoint->y & 3))
{
DPF_ERR("Destination points array coordinates must each be 4 pixel aligned for DXT surfaces. CopyRects fails");
return D3DERR_INVALIDCALL;
}
}
// Check that the dest rect (where left/top is the x/y of the point
// and the right/bottom is x+width, y+height) fits inside
// the DstDesc.
if (((pPoint->x +
(pSrcRectsArray[i].right - pSrcRectsArray[i].left)) > (int)DstDesc.Width) ||
((pPoint->y +
(pSrcRectsArray[i].bottom - pSrcRectsArray[i].top)) > (int)DstDesc.Height) ||
(pPoint->x < 0) ||
(pPoint->y < 0))
{
DPF_ERR("Destination rect is outside of the surface. CopyRects fails.");
return D3DERR_INVALIDCALL;
}
}
return InternalCopyRects(pSrc,
pSrcRectsArray,
cRects,
pDst,
pDstPointsArray);
} // CopyRects
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::InternalCopyRects"
HRESULT CBaseDevice::InternalCopyRects(CBaseSurface *pSrcSurface,
CONST RECT *pSrcRectsArray,
UINT cRects,
CBaseSurface *pDstSurface,
CONST POINT *pDstPointsArray)
{
D3DSURFACE_DESC SrcDesc = pSrcSurface->InternalGetDesc();
D3DSURFACE_DESC DstDesc = pDstSurface->InternalGetDesc();
HRESULT hr;
RECT Rect;
POINT Point;
CONST RECT* pRect;
CONST POINT* pPoint;
int BPP;
UINT i;
// If either one of these surfaces is a deep mipmap level that the
// driver can't handle, then we didn't really create it so we don't
// want to try to copy it.
if (D3D8IsDummySurface(pDstSurface->KernelHandle()) ||
D3D8IsDummySurface(pSrcSurface->KernelHandle()))
{
return D3D_OK;
}
if (pSrcRectsArray == NULL)
{
cRects = 1;
pSrcRectsArray = &Rect;
Rect.left = Rect.top = 0;
Rect.right = SrcDesc.Width;
Rect.bottom = SrcDesc.Height;
pDstPointsArray = &Point;
Point.x = Point.y = 0;
}
// Now figure out what is the best way to copy the data.
if (CanHardwareBlt(GetCoreCaps(),
SrcDesc.Pool,
SrcDesc.Format,
DstDesc.Pool,
DstDesc.Format,
GetDeviceType()))
{
// If we are setting up a blt outside of the
// the DP2 stream; then we must call Sync on the
// source and destination surfaces to make sure
// that any pending TexBlt to or from the surfaces
// or any pending triangles using these textures
// has been sent down to the driver
pSrcSurface->Sync();
pDstSurface->Sync();
if (DstDesc.Pool == D3DPOOL_SYSTEMMEM)
{
// If the destination is system-memory,
// then we need to mark it dirty. Easiest way
// is lock/unlock
D3DLOCKED_RECT LockTemp;
hr = pDstSurface->InternalLockRect(&LockTemp, NULL, 0);
if (FAILED(hr))
{
DPF_ERR("Could not lock sys-mem destination for CopyRects?");
}
else
{
hr = pDstSurface->InternalUnlockRect();
DXGASSERT(SUCCEEDED(hr));
}
}
D3D8_BLTDATA BltData;
ZeroMemory(&BltData, sizeof BltData);
BltData.hDD = GetHandle();
BltData.hDestSurface = pDstSurface->KernelHandle();
BltData.hSrcSurface = pSrcSurface->KernelHandle();
BltData.dwFlags = DDBLT_ROP | DDBLT_WAIT;
for (i = 0; i < cRects; i++)
{
if (pDstPointsArray == NULL)
{
BltData.rDest.left = pSrcRectsArray[i].left;
BltData.rDest.top = pSrcRectsArray[i].top;
}
else
{
BltData.rDest.left = pDstPointsArray[i].x;
BltData.rDest.top = pDstPointsArray[i].y;
}
BltData.rDest.right = BltData.rDest.left +
pSrcRectsArray[i].right -
pSrcRectsArray[i].left;
BltData.rDest.bottom = BltData.rDest.top +
pSrcRectsArray[i].bottom -
pSrcRectsArray[i].top;
BltData.rSrc.left = pSrcRectsArray[i].left;
BltData.rSrc.right = pSrcRectsArray[i].right;
BltData.rSrc.top = pSrcRectsArray[i].top;
BltData.rSrc.bottom = pSrcRectsArray[i].bottom;
GetHalCallbacks()->Blt(&BltData);
if (FAILED(BltData.ddRVal))
{
// We should mask errors if we are lost
// and the copy is to vidmem. Also, if
// the copy is persistent-to-persistent,
// then fail-over to our lock&copy code
// later in this function.
if (BltData.ddRVal == D3DERR_DEVICELOST)
{
if (DstDesc.Pool == D3DPOOL_MANAGED ||
DstDesc.Pool == D3DPOOL_SYSTEMMEM)
{
if (SrcDesc.Pool == D3DPOOL_MANAGED ||
SrcDesc.Pool == D3DPOOL_SYSTEMMEM)
{
// if we got here
// then it must be persistent to persistent
// so we break out of our loop
break;
}
DPF_ERR("Failing copy from video-memory surface to "
"system-memory or managed surface because "
"device is lost. CopyRect returns D3DERR_DEVICELOST");
return D3DERR_DEVICELOST;
}
else
{
// copying to vid-mem when we are lost
// can just be ignored; since the lock
// is faked anyhow
return S_OK;
}
}
}
}
// We can handle persistent-to-persistent even
// in case of loss. Other errors are fatal.
if (BltData.ddRVal != D3DERR_DEVICELOST)
{
if (FAILED(BltData.ddRVal))
{
DPF_ERR("Hardware Blt failed. CopyRects failed");
}
return BltData.ddRVal;
}
}
// We are here either because the device doesn't support Blt, or because
// the hardware blt failed due to device lost and we think that we can
// emulate it.
D3DLOCKED_RECT SrcLock;
D3DLOCKED_RECT DstLock;
BOOL bDXT = FALSE;
// We need to lock both surfaces and basically do a memcpy
BPP = CPixel::ComputePixelStride(SrcDesc.Format);
if (CPixel::IsDXT(BPP))
{
bDXT = TRUE;
BPP *= -1;
}
if (BPP == 0)
{
DPF_ERR("Format not understood - cannot perform the copy. CopyRects fails.");
return D3DERR_INVALIDCALL;
}
// CONSIDER: We should be passing D3DLOCK_NO_DIRTY_RECT
// and then call AddDirtyRect if this is part of a
// texture; probably need to add some method to CBaseSurface
// for this purpose
hr = pSrcSurface->InternalLockRect(&SrcLock, NULL, D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK);
if (FAILED(hr))
{
return hr;
}
hr = pDstSurface->InternalLockRect(&DstLock, NULL, D3DLOCK_NOSYSLOCK);
if (FAILED(hr))
{
pSrcSurface->InternalUnlockRect();
return hr;
}
// We check for DeviceLost here when copying from vidmem to sysmem since
// device lost can happen asynchronously.
if (((DstDesc.Pool == D3DPOOL_MANAGED) ||
(DstDesc.Pool == D3DPOOL_SYSTEMMEM)) &&
((SrcDesc.Pool != D3DPOOL_MANAGED) &&
(SrcDesc.Pool != D3DPOOL_SYSTEMMEM)))
{
if (D3D8IsDeviceLost(GetHandle()))
{
pSrcSurface->InternalUnlockRect();
pDstSurface->InternalUnlockRect();
return D3DERR_DEVICELOST;
}
}
pRect = pSrcRectsArray;
pPoint = pDstPointsArray;
for (i = 0; i < cRects; i++)
{
BYTE* pSrc;
BYTE* pDst;
DWORD BytesToCopy;
DWORD NumRows;
// If did not specify a dest point, then we
// will use the src (left, top) as the dest point.
if (pDstPointsArray == NULL)
{
pPoint = (POINT*) pRect;
}
// Handle DXT case inside the loop
// so that we don't have to touch the user's array
if (bDXT)
{
// Figure out our pointers by converting rect/point
// offsets to blocks
pSrc = (BYTE*)SrcLock.pBits;
pSrc += (pRect->top / 4) * SrcLock.Pitch;
pSrc += (pRect->left / 4) * BPP;
pDst = (BYTE*)DstLock.pBits;
pDst += (pPoint->y / 4) * DstLock.Pitch;
pDst += (pPoint->x / 4) * BPP;
// Convert top/bottom to blocks
DWORD top = (pRect->top) / 4;
// Handle nasty 1xN, 2xN, Nx1, Nx2 DXT cases
// by rounding.
DWORD bottom = (pRect->bottom + 3) / 4;
// For DXT formats, we know that pitch equals
// width; so we only need to check if we
// are copying an entire row to an entire
// row to go the fast path.
if ((pRect->left == 0) &&
(pRect->right == (INT)SrcDesc.Width) &&
(SrcLock.Pitch == DstLock.Pitch))
{
BytesToCopy = SrcLock.Pitch * (bottom - top);
NumRows = 1;
}
else
{
// Convert left/right to blocks
DWORD left = (pRect->left / 4);
// Round for the right -> block conversion
DWORD right = (pRect->right + 3) / 4;
BytesToCopy = (right - left) * BPP;
NumRows = bottom - top;
}
}
else
{
pSrc = (BYTE*)SrcLock.pBits +
(pRect->top * SrcLock.Pitch) +
(pRect->left * BPP);
pDst = (BYTE*)DstLock.pBits +
(pPoint->y * DstLock.Pitch) +
(pPoint->x * BPP);
// If the src and dest are linear, we can do it all in a single
// memcpy
if ((pRect->left == 0) &&
((pRect->right * BPP) == SrcLock.Pitch) &&
(SrcDesc.Width == DstDesc.Width) &&
(SrcLock.Pitch == DstLock.Pitch))
{
BytesToCopy = SrcLock.Pitch * (pRect->bottom - pRect->top);
NumRows = 1;
}
else
{
BytesToCopy = (pRect->right - pRect->left) * BPP;
NumRows = pRect->bottom - pRect->top;
}
}
// Copy the rows
DXGASSERT(NumRows > 0);
DXGASSERT(BytesToCopy > 0);
DXGASSERT(SrcLock.Pitch > 0);
DXGASSERT(DstLock.Pitch > 0);
for (UINT j = 0; j < NumRows; j++)
{
memcpy(pDst,
pSrc,
BytesToCopy);
pSrc += SrcLock.Pitch;
pDst += DstLock.Pitch;
}
// Move onward to the next rect/point pair
pRect++;
pPoint++;
}
// We check for DeviceLost yet again since it coulkd have occurred while
// copying the data.
hr = D3D_OK;
if (((DstDesc.Pool == D3DPOOL_MANAGED) ||
(DstDesc.Pool == D3DPOOL_SYSTEMMEM)) &&
((SrcDesc.Pool != D3DPOOL_MANAGED) &&
(SrcDesc.Pool != D3DPOOL_SYSTEMMEM)))
{
if (D3D8IsDeviceLost(GetHandle()))
{
hr = D3DERR_DEVICELOST;
}
}
pSrcSurface->InternalUnlockRect();
pDstSurface->InternalUnlockRect();
return hr;
} // InternalCopyRects
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::UpdateTexture"
STDMETHODIMP CBaseDevice::UpdateTexture(IDirect3DBaseTexture8 *pSrcTexture,
IDirect3DBaseTexture8 *pDstTexture)
{
API_ENTER(this);
HRESULT hr;
#ifdef DEBUG
// Some parameter validation is in Debug only for performance reasons
if (pSrcTexture == NULL || pDstTexture == NULL)
{
DPF_ERR("Invalid parameter to UpdateTexture");
return D3DERR_INVALIDCALL;
}
#endif // DEBUG
CBaseTexture *pSrcTex = CBaseTexture::SafeCast(pSrcTexture);
if (pSrcTex->Device() != this)
{
DPF_ERR("SrcTexture was not created with this Device. UpdateTexture fails");
return D3DERR_INVALIDCALL;
}
CBaseTexture *pDstTex = CBaseTexture::SafeCast(pDstTexture);
if (pDstTex->Device() != this)
{
DPF_ERR("DstTexture was not created with this Device. UpdateTexture fails");
return D3DERR_INVALIDCALL;
}
#ifdef DEBUG
// Ensure matching formats
if (pSrcTex->GetUserFormat() != pDstTex->GetUserFormat())
{
DPF_ERR("Formats of source and dest don't match. UpdateTexture fails");
return D3DERR_INVALIDCALL;
}
// Ensure matching types
if (pSrcTex->GetBufferDesc()->Type !=
pDstTex->GetBufferDesc()->Type)
{
DPF_ERR("Types of source and dest don't match. UpdateTexture fails");
return D3DERR_INVALIDCALL;
}
// Check that Source has at least as many levels as dest
if (pSrcTex->GetLevelCount() < pDstTex->GetLevelCount())
{
DPF_ERR("Source for UpdateTexture must have at least as many levels"
" as the Destination.");
return D3DERR_INVALIDCALL;
}
// Check that the source texture is not already locked
if (pSrcTex->IsTextureLocked())
{
DPF_ERR("Source for UpdateTexture is currently locked. Unlock must be called "
"before calling UpdateTexture.");
return D3DERR_INVALIDCALL;
}
// Check that the dest texture is not already locked
if (pDstTex->IsTextureLocked())
{
DPF_ERR("Destination for UpdateTexture is currently locked. Unlock must be called "
"before calling UpdateTexture.");
return D3DERR_INVALIDCALL;
}
#endif // DEBUG
// Ensure that src was specified in Pool systemmem
if (pSrcTex->GetUserPool() != D3DPOOL_SYSTEMMEM)
{
DPF_ERR("Source Texture for UpdateTexture must be in POOL_SYSTEMMEM.");
return D3DERR_INVALIDCALL;
}
// Ensure that destination was specified in Pool default
if (pDstTex->GetUserPool() != D3DPOOL_DEFAULT)
{
DPF_ERR("Destination Texture for UpdateTexture must be in POOL_DEFAULT.");
return D3DERR_INVALIDCALL;
}
#ifdef DEBUG
// Call UpdateTexture on the source which will use the
// dirty rects to move just what is needed. This
// function will also do type-specific parameter checking.
hr = pSrcTex->UpdateTexture(pDstTex);
#else // !DEBUG
// In Retail we want to call UpdateDirtyPortion directly;
// which will bypass the parameter checking
hr = pSrcTex->UpdateDirtyPortion(pDstTex);
#endif // !DEBUG
if (FAILED(hr))
{
DPF_ERR("UpdateTexture failed to copy");
return hr;
}
return hr;
} // UpdateTexture
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateTexture"
STDMETHODIMP CBaseDevice::CreateTexture(UINT Width,
UINT Height,
UINT cLevels,
DWORD dwUsage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DTexture8 **ppTexture)
{
API_ENTER(this);
if (Format == D3DFMT_UNKNOWN)
{
DPF_ERR("D3DFMT_UNKNOWN is not a valid format. CreateTexture fails.");
return D3DERR_INVALIDCALL;
}
HRESULT hr = CMipMap::Create(this,
Width,
Height,
cLevels,
dwUsage,
Format,
Pool,
ppTexture);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create a texture");
return hr;
}
return hr;
} // CreateTexture
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateVolumeTexture"
STDMETHODIMP CBaseDevice::CreateVolumeTexture(
UINT Width,
UINT Height,
UINT cpDepth,
UINT cLevels,
DWORD dwUsage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DVolumeTexture8 **ppVolumeTexture)
{
API_ENTER(this);
if (Format == D3DFMT_UNKNOWN)
{
DPF_ERR("D3DFMT_UNKNOWN is not a valid format. CreateVolumeTexture fails.");
return D3DERR_INVALIDCALL;
}
HRESULT hr = CMipVolume::Create(this,
Width,
Height,
cpDepth,
cLevels,
dwUsage,
Format,
Pool,
ppVolumeTexture);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create a volume texture");
return hr;
}
return hr;
} // CreateVolumeTexture
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateCubeTexture"
STDMETHODIMP CBaseDevice::CreateCubeTexture(UINT cpEdge,
UINT cLevels,
DWORD dwUsage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DCubeTexture8 **ppCubeMap)
{
API_ENTER(this);
if (Format == D3DFMT_UNKNOWN)
{
DPF_ERR("D3DFMT_UNKNOWN is not a valid format. CreateCubeTexture fails.");
return D3DERR_INVALIDCALL;
}
HRESULT hr = CCubeMap::Create(this,
cpEdge,
cLevels,
dwUsage,
Format,
Pool,
ppCubeMap);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create cubemap");
return hr;
}
return hr;
} // CreateCubeTexture
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateRenderTarget"
STDMETHODIMP CBaseDevice::CreateRenderTarget(UINT Width,
UINT Height,
D3DFORMAT Format,
D3DMULTISAMPLE_TYPE MultiSample,
BOOL bLockable,
IDirect3DSurface8 **ppSurface)
{
API_ENTER(this);
if (Format == D3DFMT_UNKNOWN)
{
DPF_ERR("D3DFMT_UNKNOWN is not a valid format. CreateRenderTarget fails.");
return D3DERR_INVALIDCALL;
}
HRESULT hr = CSurface::CreateRenderTarget(this,
Width,
Height,
Format,
MultiSample,
bLockable,
REF_EXTERNAL,
ppSurface);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create render-target");
return hr;
}
return hr;
} // CreateRenderTarget
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateDepthStencilSurface"
STDMETHODIMP CBaseDevice::CreateDepthStencilSurface
(UINT Width,
UINT Height,
D3DFORMAT Format,
D3DMULTISAMPLE_TYPE MultiSample,
IDirect3DSurface8 **ppSurface)
{
API_ENTER(this);
if (Format == D3DFMT_UNKNOWN)
{
DPF_ERR("D3DFMT_UNKNOWN is not a valid format. CreateDepthStencilSurface fails.");
return D3DERR_INVALIDCALL;
}
HRESULT hr = CSurface::CreateZStencil(this,
Width,
Height,
Format,
MultiSample,
REF_EXTERNAL,
ppSurface);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create zstencil surface");
return hr;
}
return hr;
} // CreateDepthStencilSurface
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateImageSurface"
STDMETHODIMP CBaseDevice::CreateImageSurface(UINT Width,
UINT Height,
D3DFORMAT Format,
IDirect3DSurface8 **ppSurface)
{
API_ENTER(this);
HRESULT hr = CSurface::CreateImageSurface(this,
Width,
Height,
Format,
REF_EXTERNAL,
ppSurface);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create image surface");
return hr;
}
return hr;
} // CreateImageSurface
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateVertexBuffer"
STDMETHODIMP CBaseDevice::CreateVertexBuffer(UINT cbLength,
DWORD dwUsage,
DWORD dwFVF,
D3DPOOL Pool,
IDirect3DVertexBuffer8 **ppVertexBuffer)
{
API_ENTER(this);
if ((dwUsage & ~D3DUSAGE_VB_VALID) != 0)
{
DPF_ERR("Invalid usage flags. CreateVertexBuffer fails.");
return D3DERR_INVALIDCALL;
}
// Warn if POOL_DEFAULT and not WRITEONLY. We do this here, because fe creates
// a VB with WRITEONLY not set and we don't want to warn in that case.
if (Pool == D3DPOOL_DEFAULT && (dwUsage & D3DUSAGE_WRITEONLY) == 0)
{
DPF(1, "Vertexbuffer created with POOL_DEFAULT but WRITEONLY not set. Performance penalty could be severe.");
}
HRESULT hr = CVertexBuffer::Create(this,
cbLength,
dwUsage,
dwFVF,
Pool,
REF_EXTERNAL,
ppVertexBuffer);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create Vertex Buffer");
return hr;
}
return hr;
} // CBaseDevice::CreateVertexBuffer
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CreateIndexBuffer"
STDMETHODIMP CBaseDevice::CreateIndexBuffer(UINT cbLength,
DWORD dwUsage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DIndexBuffer8 **ppIndexBuffer)
{
API_ENTER(this);
if ((dwUsage & ~D3DUSAGE_IB_VALID) != 0)
{
DPF_ERR("Invalid usage flags. CreateIndexBuffer fails");
return D3DERR_INVALIDCALL;
}
// Warn if POOL_DEFAULT and not WRITEONLY. We do this here, because fe creates
// a IB with WRITEONLY not set and we don't want to warn in that case.
if (Pool == D3DPOOL_DEFAULT && (dwUsage & D3DUSAGE_WRITEONLY) == 0)
{
DPF(1, "Indexbuffer created with POOL_DEFAULT but WRITEONLY not set. Performance penalty could be severe.");
}
HRESULT hr = CIndexBuffer::Create(this,
cbLength,
dwUsage,
Format,
Pool,
REF_EXTERNAL,
ppIndexBuffer);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create indexbuffer");
return hr;
}
return hr;
} // CBaseDevice::CreateIndexBuffer
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::UpdateRenderTarget"
void CBaseDevice::UpdateRenderTarget(CBaseSurface *pRenderTarget,
CBaseSurface *pZStencil)
{
// We only change things if the old and new are different;
// this is to allow the device to update itself to the
// same object without needing an extra-ref-count
// Has the RenderTarget changed?
if (pRenderTarget != m_pRenderTarget)
{
// Release old RT
if (m_pRenderTarget)
m_pRenderTarget->DecrementUseCount();
m_pRenderTarget = pRenderTarget;
if (m_pRenderTarget)
{
// IncrementUseCount the new RT
m_pRenderTarget->IncrementUseCount();
// Update the batch count for the new rendertarget
m_pRenderTarget->Batch();
}
}
// Has the Z changed?
if (m_pZBuffer != pZStencil)
{
// Release the old Z
if (m_pZBuffer)
m_pZBuffer->DecrementUseCount();
m_pZBuffer = pZStencil;
// IncrementUseCount the new Z
if (m_pZBuffer)
{
m_pZBuffer->IncrementUseCount();
// Update the batch count for the new zbuffer
m_pZBuffer->Batch();
}
}
return;
} // UpdateRenderTarget
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::CBaseDevice"
CBaseDevice::CBaseDevice()
{
// Give our base class a pointer to ourselves
SetOwner(this);
m_hwndFocusWindow = 0;
m_cRef = 1;
m_pResourceList = 0;
m_pResourceManager = new CResourceManager();
m_dwBehaviorFlags = 0;
m_dwOriginalBehaviorFlags = 0;
m_fullscreen = FALSE;
m_bVBFailOversDisabled = FALSE;
m_pZBuffer = NULL;
m_pSwapChain = NULL;
m_pRenderTarget = NULL;
m_pAutoZStencil = NULL;
m_ddiType = D3DDDITYPE_NULL;
} // CBaseDevice
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::~CBaseDevice"
CBaseDevice::~CBaseDevice()
{
DWORD cUseCount;
// Release our objects
if (m_pAutoZStencil)
{
cUseCount = m_pAutoZStencil->DecrementUseCount();
DXGASSERT(cUseCount == 0 || m_pAutoZStencil == m_pZBuffer);
}
// Mark Z buffer as no longer in use
if (m_pZBuffer)
{
cUseCount = m_pZBuffer->DecrementUseCount();
DXGASSERT(cUseCount == 0);
m_pZBuffer = NULL;
}
// Mark render target as no longer in use
if (m_pRenderTarget)
{
cUseCount = m_pRenderTarget->DecrementUseCount();
m_pRenderTarget = NULL; //so that FlipToGDISurface won't have to reset it
}
if (m_pSwapChain)
{
if (m_fullscreen)
m_pSwapChain->FlipToGDISurface();
cUseCount = m_pSwapChain->DecrementUseCount();
DXGASSERT(cUseCount == 0);
}
DD_DoneDC(m_DeviceData.hDC);
// Free allocations we made when the device was created
if (m_DeviceData.DriverData.pGDD8SupportedFormatOps != NULL)
{
MemFree(m_DeviceData.DriverData.pGDD8SupportedFormatOps);
}
// If a software driver is loaded, unload it now
if (m_DeviceData.hLibrary != NULL)
{
FreeLibrary(m_DeviceData.hLibrary);
}
// Shut down the thunk layer
D3D8DeleteDirectDrawObject(m_DeviceData.hDD);
delete m_pResourceManager;
// We release the Enum last because various destructors expect to
// be around i.e. the swapchain stuff. Also, because it is a
// stand-alone object; it should not have any dependencies on the
// the device.
if (NULL != Enum())
{
Enum()->Release();
}
} // ~CBaseDevice
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::Init"
HRESULT CBaseDevice::Init(
PD3D8_DEVICEDATA pDevice,
D3DDEVTYPE DeviceType,
HWND hwndFocusWindow,
DWORD dwBehaviorFlags,
D3DPRESENT_PARAMETERS *pPresentationParams,
UINT AdapterIndex,
CEnum *ParentClass)
{
HRESULT hr;
DWORD value = 0;
m_DeviceData = *pDevice;
m_hwndFocusWindow = hwndFocusWindow;
m_DeviceType = DeviceType;
m_AdapterIndex = AdapterIndex;
m_pD3DClass = ParentClass;
GetD3DRegValue(REG_DWORD, "DisableDM", &value, sizeof(DWORD));
#ifdef WINNT
m_dwBehaviorFlags = dwBehaviorFlags | (!IsWhistler() || value != 0 ? D3DCREATE_DISABLE_DRIVER_MANAGEMENT : 0);
#else
m_dwBehaviorFlags = dwBehaviorFlags | (value != 0 ? D3DCREATE_DISABLE_DRIVER_MANAGEMENT : 0);
#endif
value = 0;
GetD3DRegValue(REG_DWORD, "DisableST", &value, sizeof(DWORD));
m_dwOriginalBehaviorFlags = m_dwBehaviorFlags;
if (value != 0)
{
m_dwBehaviorFlags |= D3DCREATE_MULTITHREADED;
}
MemFree(pDevice); // Now that we've stored the contents, we can free the old memory
ParentClass->AddRef();
#ifndef WINNT
if (FocusWindow())
{
hr = D3D8SetCooperativeLevel(GetHandle(), FocusWindow(), DDSCL_SETFOCUSWINDOW);
if (FAILED(hr))
{
return hr;
}
}
#endif //!WINNT
//Figure out if we're a screen-saver or not.
char name[_MAX_PATH];
HMODULE hfile = GetModuleHandle( NULL );
name[0]=0;
GetModuleFileName( hfile, name, sizeof( name ) -1 );
int len = strlen(name);
if( ( strlen(name) > 4 ) &&
name[len - 4 ] == '.' &&
(name[ len - 3 ] == 's' || name[ len - 3 ] == 'S' )&&
(name[ len - 2 ] == 'c' || name[ len - 2 ] == 'C' )&&
(name[ len - 1 ] == 'r' || name[ len - 1 ] == 'R' ))
{
m_dwBehaviorFlags |= 0x10000000;
}
// Initialize our critical section (if needed)
if (m_dwBehaviorFlags & D3DCREATE_MULTITHREADED)
{
EnableCriticalSection();
}
// Initialize the resource manager
hr = ResourceManager()->Init(this);
if (hr != S_OK)
{
return hr;
}
m_DesktopMode.Height = DisplayHeight();
m_DesktopMode.Width = DisplayWidth();
m_DesktopMode.Format = DisplayFormat();
m_DesktopMode.RefreshRate = DisplayRate();
// Now call Reset to do any mode changes required and to create
// the primary surface, etc.
m_pSwapChain = new CSwapChain(
this,
REF_INTRINSIC);
if (m_pSwapChain)
{
m_pSwapChain->Init(
pPresentationParams,
&hr);
if (FAILED(hr))
return hr;
}
else
{
hr = E_OUTOFMEMORY;
return hr;
}
// If we were created with a specification for a default
// z buffer; then we need to create one here.
if (pPresentationParams->EnableAutoDepthStencil)
{
// Need to validate that this Z-buffer matches
// the HW
hr = CheckDepthStencilMatch(pPresentationParams->BackBufferFormat,
pPresentationParams->AutoDepthStencilFormat);
if (FAILED(hr))
{
DPF_ERR("AutoDepthStencilFormat does not match BackBufferFormat because "
"the current Device requires the bitdepth of the zbuffer to "
"match the render-target. See CheckDepthStencilMatch documentation. CreateDevice fails.");
return hr;
}
IDirect3DSurface8 *pSurf;
hr = CSurface::CreateZStencil(
this,
m_pSwapChain->Width(),
m_pSwapChain->Height(),
pPresentationParams->AutoDepthStencilFormat,
pPresentationParams->MultiSampleType,
REF_INTRINSIC,
&pSurf);
if (FAILED(hr))
{
DPF_ERR("Failure trying to create automatic zstencil surface. CreateDevice Failed.");
return hr;
}
m_pAutoZStencil = static_cast<CBaseSurface *>(pSurf);
}
UpdateRenderTarget(m_pSwapChain->m_ppBackBuffers[0], m_pAutoZStencil);
m_fullscreen = !SwapChain()->m_PresentationData.Windowed;
HKEY hKey;
if(ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, RESPATH_D3D, &hKey))
{
DWORD dwType;
DWORD dwValue;
DWORD dwSize = 4;
if (ERROR_SUCCESS == RegQueryValueEx(hKey, "DisableVBFailovers", NULL, &dwType, (LPBYTE) &dwValue, &dwSize) &&
dwType == REG_DWORD &&
dwValue != 0)
{
m_bVBFailOversDisabled = TRUE;
}
RegCloseKey(hKey);
}
return hr;
} // Init
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetDeviceCaps"
STDMETHODIMP CBaseDevice::GetDeviceCaps(D3DCAPS8 *pCaps)
{
API_ENTER(this);
if (pCaps == NULL)
{
DPF_ERR("Invalid pointer to D3DCAPS8 specified. GetDeviceCaps fails");
return D3DERR_INVALIDCALL;
}
Enum()->FillInCaps (
pCaps,
GetCoreCaps(),
m_DeviceType,
m_AdapterIndex);
// Emulation of NPatches is done in software when they are not supported
// for non-Pure devices.
if ((pCaps->DevCaps & D3DDEVCAPS_RTPATCHES) && (BehaviorFlags() & D3DCREATE_PUREDEVICE) == 0)
pCaps->DevCaps |= D3DDEVCAPS_NPATCHES;
// Now the Caps struct has all the hardware caps.
// In case the device is running in a software vertex-processing mode
// fix up the caps to reflect that.
if( ((BehaviorFlags() & D3DCREATE_PUREDEVICE) == 0)
&&
(static_cast<CD3DHal *>(this))->m_dwRuntimeFlags &
D3DRT_RSSOFTWAREPROCESSING )
{
// We always do TL Vertex clipping for software vertex processing.
pCaps->PrimitiveMiscCaps |= D3DPMISCCAPS_CLIPTLVERTS;
pCaps->RasterCaps |= (D3DPRASTERCAPS_FOGVERTEX |
D3DPRASTERCAPS_FOGRANGE);
// We do emulation when FVF has point size but the device does not
// support it
pCaps->FVFCaps |= D3DFVFCAPS_PSIZE;
// All DX8 drivers have to support this cap.
// Emulation is provided by the software vertex pipeline for all
// pre-DX8 drivers.
if( pCaps->MaxPointSize == 0 )
{
pCaps->MaxPointSize = 64; // __MAX_POINT_SIZE in d3ditype.h
}
pCaps->MaxActiveLights = 0xffffffff;
pCaps->MaxVertexBlendMatrices = 4;
pCaps->MaxUserClipPlanes = 6; // __MAXUSERCLIPPLANES in d3dfe.hpp
pCaps->VertexProcessingCaps = (D3DVTXPCAPS_TEXGEN |
D3DVTXPCAPS_MATERIALSOURCE7 |
D3DVTXPCAPS_DIRECTIONALLIGHTS |
D3DVTXPCAPS_POSITIONALLIGHTS |
D3DVTXPCAPS_LOCALVIEWER |
D3DVTXPCAPS_TWEENING);
pCaps->MaxVertexBlendMatrixIndex = 255; // __MAXWORLDMATRICES - 1 in
// d3dfe.hpp
pCaps->MaxStreams = 16; // __NUMSTREAMS in d3dfe.hpp
pCaps->VertexShaderVersion = D3DVS_VERSION(1, 1); // Version 1.1
pCaps->MaxVertexShaderConst = D3DVS_CONSTREG_MAX_V1_1;
// Nuke NPATCHES and RT Patches caps, because software emulation
// cannot do that.
pCaps->DevCaps &= ~(D3DDEVCAPS_NPATCHES | D3DDEVCAPS_RTPATCHES);
}
// MaxPointSize should never be reported as Zero. Internally though
// we depend on Zero to be what decides to take the point-sprite emulation
// path or not.
// If it is still zero at this point, fudge it up here.
if( pCaps->MaxPointSize == 0 )
{
pCaps->MaxPointSize = 1.0f;
}
return D3D_OK;
} // GetDeviceCaps
#undef DPF_MODNAME
#define DPF_MODNAME "CBaseDevice::GetDeviceCaps"
STDMETHODIMP CBaseDevice::GetFrontBuffer(IDirect3DSurface8 *pDSurface)
{
API_ENTER(this);
RECT Rect;
D3DSURFACE_DESC SurfDesc;
CDriverSurface* pPrimary;
D3DLOCKED_RECT PrimaryRect;
D3DLOCKED_RECT DestRect;
HRESULT hr;
D3DFORMAT Format;
UINT Width;
UINT Height;
BYTE* pSrc;
BYTE* pDest;
DWORD* pDstTemp;
BYTE* pSrc8;
WORD* pSrc16;
DWORD* pSrc32;
UINT i;
UINT j;
PALETTEENTRY Palette[256];
if (pDSurface == NULL)
{
DPF_ERR("Invalid pointer to destination surface specified. GetFrontBuffer fails.");
return D3DERR_INVALIDCALL;
}
CBaseSurface *pDestSurface = static_cast<CBaseSurface*>(pDSurface);
if (pDestSurface->InternalGetDevice() != this)
{
DPF_ERR("Destination Surface was not allocated with this Device. GetFrontBuffer fails. ");
return D3DERR_INVALIDCALL;
}
hr = pDestSurface->GetDesc(&SurfDesc);
DXGASSERT(SUCCEEDED(hr));
if (SurfDesc.Format != D3DFMT_A8R8G8B8)
{
DPF_ERR("Destination surface must have format D3DFMT_A8R8G8B8. GetFrontBuffer fails.");
return D3DERR_INVALIDCALL;
}
if (SurfDesc.Type != D3DRTYPE_SURFACE)
{
DPF_ERR("Destination surface is an invalid type. GetFrontBuffer fails.");
return D3DERR_INVALIDCALL;
}
if ( (SurfDesc.Pool != D3DPOOL_SYSTEMMEM) && (SurfDesc.Pool != D3DPOOL_SCRATCH))
{
DPF_ERR("Destination surface must be in system or scratch memory. GetFrontBuffer fails.");
return D3DERR_INVALIDCALL;
}
Rect.left = Rect.top = 0;
Rect.right = DisplayWidth();
Rect.bottom = DisplayHeight();
if ((SurfDesc.Width < (UINT)(Rect.right - Rect.left)) ||
(SurfDesc.Height < (UINT)(Rect.bottom - Rect.top)))
{
DPF_ERR("Destination surface not big enough to hold the size of the screen. GetFrontBuffer fails.");
return D3DERR_INVALIDCALL;
}
if (NULL == m_pSwapChain)
{
DPF_ERR("No Swap Chain present, GetFrontBuffer fails.");
return D3DERR_INVALIDCALL;
}
// Lock the primary surface
pPrimary = m_pSwapChain->PrimarySurface();
if (NULL == pPrimary)
{
DPF_ERR("No Primary present, GetFrontBuffer fails");
return D3DERR_DEVICELOST;
}
hr = pPrimary->LockRect(&PrimaryRect,
NULL,
0);
if (SUCCEEDED(hr))
{
hr = pDestSurface->LockRect(&DestRect,
NULL,
0);
if (FAILED(hr))
{
DPF_ERR("Unable to lock destination surface. GetFrontBuffer fails.");
pPrimary->UnlockRect();
return hr;
}
Format = DisplayFormat();
Width = Rect.right;
Height = Rect.bottom;
pSrc = (BYTE*) PrimaryRect.pBits;
pDest = (BYTE*) DestRect.pBits;
if (Format == D3DFMT_P8)
{
HDC hdc;
hdc = GetDC (NULL);
GetSystemPaletteEntries(hdc, 0, 256, Palette);
ReleaseDC (NULL, hdc);
}
for (i = 0; i < Height; i++)
{
pDstTemp = (DWORD*) pDest;
switch (Format)
{
case D3DFMT_P8:
pSrc8 = pSrc;
for (j = 0; j < Width; j++)
{
*pDstTemp = (Palette[*pSrc8].peRed << 16) |
(Palette[*pSrc8].peGreen << 8) |
(Palette[*pSrc8].peBlue);
pSrc8++;
pDstTemp++;
}
break;
case D3DFMT_R5G6B5:
pSrc16 = (WORD*) pSrc;
for (j = 0; j < Width; j++)
{
DWORD dwTemp = ((*pSrc16 & 0xf800) << 8) |
((*pSrc16 & 0x07e0) << 5) |
((*pSrc16 & 0x001f) << 3);
// Need to tweak ranges so that
// we map entirely to the 0x00 to 0xff
// for each channel. Basically, we
// map the high two/three bits of each
// channel to fill the gap at the bottom.
dwTemp |= (dwTemp & 0x00e000e0) >> 5;
dwTemp |= (dwTemp & 0x0000c000) >> 6;
// Write out our value
*pDstTemp = dwTemp;
pDstTemp++;
pSrc16++;
}
break;
case D3DFMT_X1R5G5B5:
pSrc16 = (WORD*) pSrc;
for (j = 0; j < Width; j++)
{
DWORD dwTemp= ((*pSrc16 & 0x7c00) << 9) |
((*pSrc16 & 0x03e0) << 6) |
((*pSrc16 & 0x001f) << 3);
// Need to tweak ranges so that
// we map entirely to the 0x00 to 0xff
// for each channel. Basically, we
// map the high three bits of each
// channel to fill the gap at the bottom.
dwTemp |= (dwTemp & 0x00e0e0e0) >> 5;
// Write out our value
*pDstTemp = dwTemp;
pDstTemp++;
pSrc16++;
}
break;
case D3DFMT_R8G8B8:
pSrc8 = pSrc;
for (j = 0; j < Width; j++)
{
*pDstTemp = (pSrc8[0] << 16) |
(pSrc8[1] << 8) |
(pSrc8[2]);
pDstTemp++;
pSrc8 += 3;
}
break;
case D3DFMT_X8R8G8B8:
pSrc32 = (DWORD*) pSrc;
for (j = 0; j < Width; j++)
{
*pDstTemp = *pSrc32 & 0xffffff;
pDstTemp++;
pSrc32++;
}
break;
default:
DXGASSERT(0);
pDestSurface->UnlockRect();
pPrimary->UnlockRect();
return D3DERR_INVALIDCALL;
}
pSrc += PrimaryRect.Pitch;
pDest += DestRect.Pitch;
}
pDestSurface->UnlockRect();
pPrimary->UnlockRect();
}
return hr;
}
// End of file : dxgcreate.cpp