2370 lines
71 KiB
C++
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© 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
|