/*========================================================================== * * 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(static_cast(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 (*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(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(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(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(this)->Destroy(); UpdateRenderTarget(m_pSwapChain->m_ppBackBuffers[0], m_pAutoZStencil); hr = static_cast(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(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(pSrcSurface); if (pSrc->InternalGetDevice() != this) { DPF_ERR("SrcSurface was not allocated with this Device. CopyRect fails."); return D3DERR_INVALIDCALL; } CBaseSurface *pDst = static_cast(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(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(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(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