//============================================================================= // Copyright (c) 1999 Microsoft Corporation // // swapchan.cpp // // Direct3D swap chain implementation. // // Created 11/16/1999 johnstep (John Stephens) //============================================================================= #include "ddrawpr.h" #include "swapchan.hpp" #include "pixel.hpp" #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::QueryInterface" //============================================================================= // IUnknown::QueryInterface (public) // // Created 11/16/1999 johnstep //============================================================================= STDMETHODIMP CSwapChain::QueryInterface( REFIID riid, void **ppInterface ) { API_ENTER(Device()); if (!VALID_PTR_PTR(ppInterface)) { DPF_ERR("Invalid ppvObj parameter passed to QueryInterface for a SwapChain"); return D3DERR_INVALIDCALL; } if (!VALID_PTR(&riid, sizeof(GUID))) { DPF_ERR("Invalid guid memory address to QueryInterface for a SwapChain"); return D3DERR_INVALIDCALL; } if ((riid == IID_IDirect3DSwapChain8) || (riid == IID_IUnknown)) { AddRef(); *ppInterface = static_cast( static_cast(this)); return S_OK; } DPF_ERR("Unsupported Interface identifier passed to QueryInterface for a SwapChain"); // Null out param *ppInterface = NULL; return E_NOINTERFACE; } // QueryInterface #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::AddRef" //============================================================================= // IUnknown::AddRef (public) // // Created 11/16/1999 johnstep //============================================================================= STDMETHODIMP_(ULONG) CSwapChain::AddRef() { API_ENTER_NO_LOCK(Device()); return AddRefImpl(); } // AddRef #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::Release" //============================================================================= // IUnknown::Release (public) // // Created 11/16/1999 johnstep //============================================================================= STDMETHODIMP_(ULONG) CSwapChain::Release() { API_ENTER_SUBOBJECT_RELEASE(Device()); return ReleaseImpl(); } // Release #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::CSwapChain" //============================================================================= // CSwapChain::CSwapChain // // Created 11/16/1999 johnstep //============================================================================= CSwapChain::CSwapChain( CBaseDevice *pDevice, REF_TYPE refType) : CBaseObject(pDevice, refType), m_pCursor(NULL), m_hGDISurface(NULL), m_pMirrorSurface(NULL), m_pPrimarySurface(NULL), m_ppBackBuffers(NULL), m_presentnext(0), m_cBackBuffers(0), m_bExclusiveMode(FALSE), m_pCursorShadow(NULL), m_pHotTracking(NULL), m_lIMEState(0), m_lSetIME(0), m_dwFlags(0), m_dwFlipCnt(0), m_dwFlipTime(0xffffffff), m_uiErrorMode(0) { HKEY hKey; ZeroMemory(&m_BltData, sizeof m_BltData); m_BltData.hDD = Device()->GetHandle(); m_BltData.bltFX.dwROP = SRCCOPY; m_BltData.ddRVal = E_FAIL; if (!RegOpenKey(HKEY_LOCAL_MACHINE, RESPATH_D3D, &hKey)) { DWORD type; DWORD value; DWORD cb = sizeof(value); if (!RegQueryValueEx(hKey, REGSTR_VAL_DDRAW_SHOWFRAMERATE, NULL, &type, (CONST LPBYTE)&value, &cb)) { DPF( 2, REGSTR_VAL_DDRAW_SHOWFRAMERATE" : %d", value ); if (value) { m_dwFlags |= D3D_REGFLAGS_SHOWFRAMERATE; } } #ifdef WINNT cb = sizeof(value); if (!RegQueryValueEx(hKey, REGSTR_VAL_D3D_FLIPNOVSYNC, NULL, &type, (CONST LPBYTE)&value, &cb)) { DPF( 2, REGSTR_VAL_D3D_FLIPNOVSYNC" : %d", value ); if (value) { m_dwFlags |= D3D_REGFLAGS_FLIPNOVSYNC; } } RegCloseKey(hKey); } m_dwForceRefreshRate = 0; if( !RegOpenKey( HKEY_LOCAL_MACHINE, REGSTR_PATH_DDRAW, &hKey ) ) { DWORD type; DWORD value; DWORD cb = sizeof(value); if( !RegQueryValueEx( hKey, REGSTR_VAL_DDRAW_FORCEREFRESHRATE, NULL, &type, (CONST LPBYTE)&value, &cb ) ) { m_dwForceRefreshRate = value; } #endif RegCloseKey(hKey); } } void CSwapChain::Init( D3DPRESENT_PARAMETERS* pPresentationParams, HRESULT *pHr ) { DXGASSERT(pHr != NULL); //the gamma ramp is initialized to 1:1 for (int i=0;i<256;i++) { m_DesiredGammaRamp.red[i] = m_DesiredGammaRamp.green[i] = m_DesiredGammaRamp.blue[i] = static_cast(i); } *pHr = Reset( pPresentationParams); return; } // CSwapChain::CSwapChain #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::~CSwapChain" //============================================================================= // CSwapChain::~CSwapChain // // Created 11/16/1999 johnstep //============================================================================= CSwapChain::~CSwapChain() { if (!m_PresentationData.Windowed) { m_PresentationData.Windowed = TRUE; // make sure we restore after a fullscreen SetCooperativeLevel(); } Destroy(); } // CSwapChain::~CSwapChain #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::CreateWindowed" //============================================================================= // CSwapChain::CreateWindowed // // Created 11/16/1999 johnstep //============================================================================= HRESULT CSwapChain::CreateWindowed( UINT width, UINT height, D3DFORMAT backBufferFormat, UINT cBackBuffers, D3DMULTISAMPLE_TYPE MultiSampleType, BOOL bDiscard, BOOL bLockable ) { // For the windowed case, we will currently create exactly 3 surfaces // in this order: // // 1. Primary Surface // 2. Back Buffer // 3. Z Buffer (by notifying the device) // // For now, all 3 surfaces must reside in local video memory. Later, // we may allow multiple back buffers, if there is interest, to allow // simulated fullscreen-style double buffering (alternate back buffer // surfaces after presenting). // // !!! The primary surface is actually optional because we can just use // DdGetDC and then GDI BitBlt. But, presumably, there are performance // advantages to using DirectDraw Blt. Of course, the problem with using // DirectDraw Blts is keeping in sync with the window manager (so we // don't write outside of the window, etc.). GDI BitBlt will also handle // format conversion for us, though slowly. DXGASSERT(m_pPrimarySurface == NULL); DXGASSERT(m_ppBackBuffers == NULL); DXGASSERT(m_cBackBuffers == 0); HRESULT hr; if (D3DSWAPEFFECT_FLIP == m_PresentationData.SwapEffect) { cBackBuffers = m_PresentationData.BackBufferCount + 1; } // 1. Create a simple primary surface. If we already have a primary // surface, then don't bother to create a new one. if (this == Device()->SwapChain()) { DWORD dwUsage = D3DUSAGE_PRIMARYSURFACE | D3DUSAGE_LOCK; DWORD Width = Device()->DisplayWidth(); DWORD Height = Device()->DisplayHeight(); // D3DSWAPEFFECT_NO_PRESENT is a hack to allow our D3D test framework // to create a windowed REF device after they have created a fullscreen // HAL device. We cannot create a second primary surface for the windowed // device, but we cannot leave m_pPrimarySurface equal to NULL (becaue then // we cannot cleanup the swap chain and Reset will fail), so instead we // create a dummy surface and call it the primary, with the understanding // that this device will never call Present or use the primary surface in // any way. We also don't have to do much checking since this is not an // external feature. if (D3DSWAPEFFECT_NO_PRESENT == m_PresentationData.SwapEffect) { dwUsage = D3DUSAGE_OFFSCREENPLAIN | D3DUSAGE_LOCK; Width = 256; Height = 256; } m_pPrimarySurface = new CDriverSurface( Device(), Width, Height, dwUsage, Device()->DisplayFormat(), // UserFormat Device()->DisplayFormat(), // RealFormat D3DMULTISAMPLE_NONE,//of course, when windowed 0, // hKernelHandle REF_INTERNAL, &hr); } else { // Additional SwapChain, it's already there m_pPrimarySurface = Device()->SwapChain()->PrimarySurface(); hr = DD_OK; } // 2. Create the back buffer. if (m_pPrimarySurface == NULL) { return E_OUTOFMEMORY; } m_PresentUseBlt = TRUE; if (SUCCEEDED(hr)) { if (m_ppBackBuffers = new CDriverSurface *[cBackBuffers]) { DWORD Usage = D3DUSAGE_BACKBUFFER | D3DUSAGE_RENDERTARGET; if ((D3DMULTISAMPLE_NONE == m_PresentationData.MultiSampleType) && bLockable) { Usage |= D3DUSAGE_LOCK; } if (bDiscard) { Usage |= D3DUSAGE_DISCARD; } for (; m_cBackBuffers < cBackBuffers; ++m_cBackBuffers) { m_ppBackBuffers[m_cBackBuffers] = new CDriverSurface( Device(), width, height, Usage, backBufferFormat, // UserFormat backBufferFormat, // RealFormat MultiSampleType, 0, // hKernelHandle REF_INTRINSIC, &hr); if (m_ppBackBuffers[m_cBackBuffers] == NULL) { hr = E_OUTOFMEMORY; break; } if (FAILED(hr)) { m_ppBackBuffers[m_cBackBuffers]->DecrementUseCount(); m_ppBackBuffers[m_cBackBuffers] = NULL; break; } } if (m_cBackBuffers == cBackBuffers) { m_BltData.hDestSurface = m_pPrimarySurface->KernelHandle(); m_BltData.dwFlags = DDBLT_WINDOWCLIP | DDBLT_ROP | DDBLT_WAIT | DDBLT_DX8ORHIGHER; if (Device()->GetD3DCaps()->MaxStreams != 0) { m_BltData.dwFlags |= DDBLT_PRESENTATION; } if (D3DSWAPEFFECT_COPY_VSYNC == m_PresentationData.SwapEffect) { m_BltData.dwFlags |= DDBLT_COPYVSYNC; // Need to let thunk layer know current refresh rate if (Device()->DisplayRate() < 60) { // 60Hz = 16.666ms per frame // 75Hz = 13.333ms // 85Hz = 11.765ms m_BltData.threshold = 13; } else { m_BltData.threshold = (DWORD)(1000.0f / (float)Device()->DisplayRate()); } } m_ClientWidth = 0; // windowed client is updated in present m_ClientHeight = 0; return hr; } // Something went wrong, so clean up now. // 2. Destroy Back Buffers, if any. while (m_cBackBuffers > 0) { m_ppBackBuffers[--m_cBackBuffers]->DecrementUseCount(); } delete [] m_ppBackBuffers; m_ppBackBuffers = NULL; } else { hr = E_OUTOFMEMORY; } } // 1. Destroy Primary Surface. if (this == Device()->SwapChain()) m_pPrimarySurface->DecrementUseCount(); m_pPrimarySurface = NULL; return hr; } // CreateWindowed #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::CreateFullScreen" //============================================================================= // CSwapChain::CreateFullscreen // // Created 11/16/1999 johnstep //============================================================================= HRESULT CSwapChain::CreateFullscreen( UINT width, UINT height, D3DFORMAT backBufferFormat, UINT cBackBuffers, UINT PresentationRate, D3DMULTISAMPLE_TYPE MultiSampleType, BOOL bDiscard, BOOL bLockable ) { HRESULT hr = E_FAIL; DWORD i; BOOL bMirrorBufferCreated, bNoDDrawSupport; UINT Usage = 0; if (bLockable) { Usage |= D3DUSAGE_LOCK; } if (bDiscard) { Usage |= D3DUSAGE_DISCARD; } // If it's a hardware device, we want to create a primary surface and a // number of backbuffers. We need to make this a single driver call, // however, in order for everything to get attached correctly. Therefore, // what we do is call the DDI to create the primary chain, and it will // return the handles for each surface in the chain. After that, we // will individually create each swap chain buffer, but we will supply // it with the required handles rather than having it call the DDI itself. // First, call the DDI to allocate the memory and the kernel handles DDSURFACEINFO SurfInfoArray[D3DPRESENT_BACK_BUFFERS_MAX + 2]; ZeroMemory(SurfInfoArray, sizeof(SurfInfoArray)); D3D8_CREATESURFACEDATA CreateSurfaceData; ZeroMemory(&CreateSurfaceData, sizeof(CreateSurfaceData)); DXGASSERT(cBackBuffers <= D3DPRESENT_BACK_BUFFERS_MAX); for (i = 0; i < cBackBuffers + 1; i++) { SurfInfoArray[i].cpWidth = width; SurfInfoArray[i].cpHeight = height; } CreateSurfaceData.hDD = Device()->GetHandle(); CreateSurfaceData.pSList = &SurfInfoArray[0]; bNoDDrawSupport = Device()->Enum()->NoDDrawSupport(Device()->AdapterIndex()); if (D3DDEVTYPE_HAL == Device()->GetDeviceType() && m_PresentationData.SwapEffect == D3DSWAPEFFECT_FLIP) { m_PresentUseBlt = FALSE; bMirrorBufferCreated = FALSE; CreateSurfaceData.dwSCnt = cBackBuffers + 1; CreateSurfaceData.MultiSampleType = MultiSampleType; } else if ((m_PresentationData.SwapEffect == D3DSWAPEFFECT_COPY && m_PresentationData.FullScreen_PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE) || bNoDDrawSupport ) { // If we're doing a copy-swap-effect and the app // specifies interval-immediate, then we can blt directly // to the primary without a mirror. DXGASSERT(MultiSampleType == D3DMULTISAMPLE_NONE); m_PresentUseBlt = TRUE; bMirrorBufferCreated = FALSE; CreateSurfaceData.dwSCnt = 1; CreateSurfaceData.MultiSampleType = D3DMULTISAMPLE_NONE; } else { //one for m_pPrimarySurface and one for m_pMirrorSurface m_PresentUseBlt = TRUE; bMirrorBufferCreated = TRUE; CreateSurfaceData.dwSCnt = 2; CreateSurfaceData.MultiSampleType = D3DMULTISAMPLE_NONE; } CreateSurfaceData.Type = D3DRTYPE_SURFACE; CreateSurfaceData.Pool = D3DPOOL_LOCALVIDMEM; CreateSurfaceData.dwUsage = D3DUSAGE_PRIMARYSURFACE | Usage; CreateSurfaceData.Format = Device()->DisplayFormat(); if(Device()->DisplayFormat() != backBufferFormat) { CreateSurfaceData.dwUsage |= D3DUSAGE_ALPHACHANNEL; } if (!bNoDDrawSupport) { hr = Device()->GetHalCallbacks()->CreateSurface(&CreateSurfaceData); if (FAILED(hr)) { if (D3DDEVTYPE_HAL == Device()->GetDeviceType()) { DPF_ERR("Failed to create driver primary surface chain"); return hr; } else { // assume CreateSurfaceData is still intact bMirrorBufferCreated = FALSE; CreateSurfaceData.dwSCnt = 1; hr = Device()->GetHalCallbacks()->CreateSurface(&CreateSurfaceData); if (FAILED(hr)) { DPF_ERR("Failed to create driver primary surface"); return hr; } } } } // Now that we have the handles, create the surface interfaces // one by one // When creating passing in kernel handles to a driver // surface, the surface will assume that it was created in // LocalVidMem. We assert this here.. DXGASSERT(CreateSurfaceData.Pool == D3DPOOL_LOCALVIDMEM); m_pPrimarySurface = new CDriverSurface( Device(), width, height, CreateSurfaceData.dwUsage | D3DUSAGE_LOCK, // there is a problem with thunklayer when NoDDrawSupport // DriverData.DisplayWidth and DriverData.DisplayHeight // DriverData.DisplayFormat are not getting updated // so we use backBufferFormat until Device()->DisplayFormat() bNoDDrawSupport ? backBufferFormat : Device()->DisplayFormat(), // UserFormat bNoDDrawSupport ? backBufferFormat : Device()->DisplayFormat(), // RealFormat CreateSurfaceData.MultiSampleType, SurfInfoArray[0].hKernelHandle, REF_INTERNAL, &hr); if (m_pPrimarySurface == NULL) { // We'll clean up the kernel handle(s) at the // end of the function hr = E_OUTOFMEMORY; } else { // Zero out the kernel-handle so that // we don't clean it at exit. If the CDriverSurface // function fails; it will still release the kernel // handle in its destructor. SurfInfoArray[0].hKernelHandle = 0; } if (SUCCEEDED(hr)) { m_hGDISurface = m_pPrimarySurface->KernelHandle(); if (bMirrorBufferCreated) { // Mirror surfaces are only useful if we're going // to do a Blt as part of the Present. (We might // also do a flip in addition.) DXGASSERT(m_PresentUseBlt); // When creating passing in kernel handles to a driver // surface, the surface will assume that it was created in // LocalVidMem. We assert this here.. DXGASSERT(CreateSurfaceData.Pool == D3DPOOL_LOCALVIDMEM); m_pMirrorSurface = new CDriverSurface( Device(), width, height, D3DUSAGE_BACKBUFFER, Device()->DisplayFormat(), // UserFormat Device()->DisplayFormat(), // RealFormat D3DMULTISAMPLE_NONE, SurfInfoArray[1].hKernelHandle, REF_INTERNAL, &hr); if (NULL == m_pMirrorSurface) { //if out of memory, then destroy the driver object as well D3D8_DESTROYSURFACEDATA DestroySurfData; DestroySurfData.hDD = Device()->GetHandle(); DestroySurfData.hSurface = SurfInfoArray[1].hKernelHandle; Device()->GetHalCallbacks()->DestroySurface(&DestroySurfData); bMirrorBufferCreated = FALSE; hr = S_OK; //but don't fail as m_pMirrorSurface is optional } else if (FAILED(hr)) { // Release the surface m_pMirrorSurface->DecrementUseCount(); m_pMirrorSurface = NULL; bMirrorBufferCreated = FALSE; hr = S_OK; //but don't fail as m_pMirrorSurface is optional } else { //blt from m_ppBackBuffers[m_presentnext] to m_pMirrorSurface //then flip from m_pPrimarySurface to m_pMirrorSurface m_BltData.hDestSurface = m_pMirrorSurface->KernelHandle(); } // In all cases, zero out the kernel handle // since it has either been owned by something or freed by now SurfInfoArray[1].hKernelHandle = 0; } if (m_PresentUseBlt) { if (!bMirrorBufferCreated) { // If we're blitting and there is no // mirror surface; then the primary must be // the destination DXGASSERT(m_BltData.hDestSurface == NULL); m_BltData.hDestSurface = m_pPrimarySurface->KernelHandle(); } if (D3DSWAPEFFECT_FLIP == m_PresentationData.SwapEffect) { // To emualte flip for SW drivers, create an extra backbuffer cBackBuffers = m_PresentationData.BackBufferCount + 1; } ZeroMemory(SurfInfoArray, sizeof(SurfInfoArray)); for (i = 1; i < cBackBuffers + 1; i++) { SurfInfoArray[i].cpWidth = width; SurfInfoArray[i].cpHeight = height; } ZeroMemory(&CreateSurfaceData, sizeof(CreateSurfaceData)); CreateSurfaceData.hDD = Device()->GetHandle(); CreateSurfaceData.pSList = &SurfInfoArray[1]; CreateSurfaceData.dwSCnt = cBackBuffers; CreateSurfaceData.Type = D3DRTYPE_SURFACE; CreateSurfaceData.Pool = D3DPOOL_DEFAULT; CreateSurfaceData.dwUsage = D3DUSAGE_BACKBUFFER | D3DUSAGE_RENDERTARGET; CreateSurfaceData.Format = backBufferFormat; CreateSurfaceData.MultiSampleType = MultiSampleType; hr = Device()->GetHalCallbacks()->CreateSurface(&CreateSurfaceData); } } if (SUCCEEDED(hr)) { if (m_ppBackBuffers = new CDriverSurface *[cBackBuffers]) { DWORD Usage = D3DUSAGE_BACKBUFFER | D3DUSAGE_RENDERTARGET; if ((D3DMULTISAMPLE_NONE == m_PresentationData.MultiSampleType) && bLockable) { Usage |= D3DUSAGE_LOCK; } for (; m_cBackBuffers < cBackBuffers; ++m_cBackBuffers) { m_ppBackBuffers[m_cBackBuffers] = new CDriverSurface( Device(), width, height, Usage, backBufferFormat, backBufferFormat, MultiSampleType, SurfInfoArray[m_cBackBuffers + 1].hKernelHandle, REF_INTRINSIC, &hr); if (m_ppBackBuffers[m_cBackBuffers] == NULL) { // We'll clean up the kernel handle at the ned // of the function hr = E_OUTOFMEMORY; break; } else { // Zero out the kernel-handle so that // we don't clean it at exit. (Even in failure, // the m_ppBackBuffers[m_cBackBuffers] object // will free the kernel handle now SurfInfoArray[m_cBackBuffers + 1].hKernelHandle = 0; } if (FAILED(hr)) { m_ppBackBuffers[m_cBackBuffers]->DecrementUseCount(); m_ppBackBuffers[m_cBackBuffers] = NULL; break; } } if (m_cBackBuffers != cBackBuffers) { // Something went wrong, so clean up now. // 2. Destroy Back Buffers, if any. while (m_cBackBuffers > 0) { m_ppBackBuffers[--m_cBackBuffers]->DecrementUseCount(); } delete [] m_ppBackBuffers; m_ppBackBuffers = NULL; } else { const D3D8_DRIVERCAPS* pDriverCaps = Device()->GetCoreCaps(); m_dwFlipFlags = DDFLIP_WAIT; if ((D3DPRESENT_INTERVAL_IMMEDIATE == m_PresentationData.FullScreen_PresentationInterval) #ifdef WINNT || (D3D_REGFLAGS_FLIPNOVSYNC & m_dwFlags) #endif ) { if (DDCAPS2_FLIPNOVSYNC & pDriverCaps->D3DCaps.Caps2) { m_dwFlipFlags |= DDFLIP_NOVSYNC; } } else if (DDCAPS2_FLIPINTERVAL & pDriverCaps->D3DCaps.Caps2) { switch(m_PresentationData.FullScreen_PresentationInterval) { case D3DPRESENT_INTERVAL_DEFAULT: case D3DPRESENT_INTERVAL_ONE: m_dwFlipFlags |= DDFLIP_INTERVAL1; break; case D3DPRESENT_INTERVAL_TWO: m_dwFlipFlags |= DDFLIP_INTERVAL2; break; case D3DPRESENT_INTERVAL_THREE: m_dwFlipFlags |= DDFLIP_INTERVAL3; break; case D3DPRESENT_INTERVAL_FOUR: m_dwFlipFlags |= DDFLIP_INTERVAL4; break; } } m_BltData.hWnd = m_PresentationData.hDeviceWindow; m_BltData.dwFlags = DDBLT_ROP | DDBLT_WAIT; m_ClientWidth = width; m_ClientHeight = height; } } else { hr = E_OUTOFMEMORY; } } // Error handling cleanup if (FAILED(hr)) { // We may need to free surface handles that // were not owned by any CDriverSurface that // we failed to properly create D3D8_DESTROYSURFACEDATA DestroyData; ZeroMemory(&DestroyData, sizeof DestroyData); DestroyData.hDD = Device()->GetHandle(); for (UINT i = 0; i < CreateSurfaceData.dwSCnt; i++) { if (CreateSurfaceData.pSList[i].hKernelHandle) { DestroyData.hSurface = CreateSurfaceData.pSList[i].hKernelHandle; Device()->GetHalCallbacks()->DestroySurface(&DestroyData); } } } return hr; } // CreateFullScreen #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::Destroy" //============================================================================= // CSwapChain::Destroy // // Created 11/16/1999 johnstep //============================================================================= VOID CSwapChain::Destroy() { // Destroy surfaces in reverse create order. if (m_pCursor) { delete m_pCursor; m_pCursor = NULL; } // 2. Destroy Back Buffers // // If the previous mode was windowed, we should have exactly 1 // back buffer to destroy. Otherwise, there could be more than // one, and plus we may need some sort of atomic destruction. if (m_ppBackBuffers) { while (m_cBackBuffers > 0) { m_ppBackBuffers[--m_cBackBuffers]->DecrementUseCount(); } delete [] m_ppBackBuffers; m_ppBackBuffers = NULL; } // 1. Destroy Mirror Surface if (m_pMirrorSurface) { m_pMirrorSurface->DecrementUseCount(); m_pMirrorSurface = NULL; } // 1. Destroy Primary Surface if (m_pPrimarySurface) { if (this == Device()->SwapChain()) m_pPrimarySurface->DecrementUseCount(); m_pPrimarySurface = NULL; m_hGDISurface = NULL; } m_presentnext = 0; } // Destroy #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::GetBackBuffer" //============================================================================= // IDirect3DSwapChain8::GetBackBuffer (public) // // Created 11/16/1999 johnstep //============================================================================= STDMETHODIMP CSwapChain::GetBackBuffer( UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8 **ppBackBuffer ) { API_ENTER(Device()); if (ppBackBuffer == NULL) { DPF_ERR("Invalid ppBackbuffer parameter passed to GetBackBuffer"); return D3DERR_INVALIDCALL; } // We can't just assert we have a valid back buffer array because a // Reset may have failed, which puts the device in a disabled state // until Reset is called again. Once we have a `disabled' flag, we // can check that instead of m_ppBackBuffers. if (m_ppBackBuffers == NULL) { DPF_ERR("GetBackBuffer failed due to Device being lost"); return D3DERR_INVALIDCALL; } // in case of windowed D3DSWAPEFFECT_FLIP, m_cBackBuffers // == m_PresentationData.BackBufferCount + 1 as we allocate // that extra buffer for user without its knowledge if (iBackBuffer >= m_PresentationData.BackBufferCount) { DPF_ERR("Invalid iBackBuffer parameter passed to GetBackBuffer"); return D3DERR_INVALIDCALL; } *ppBackBuffer = BackBuffer(iBackBuffer); DXGASSERT(*ppBackBuffer != NULL); if (*ppBackBuffer) { (*ppBackBuffer)->AddRef(); return S_OK; } else { DPF(2, "Swapchain doesn't have a BackBuffer[%d]",iBackBuffer); return D3DERR_NOTFOUND; } } // GetBackBuffer #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::Reset" //============================================================================= // IDirect3DSwapChain8::Reset (public) // // Resizes the device. If this results in a display mode change, then all // existing surfaces will be lost. // // Arguments: // width // height // pcBackBuffers (in/out) !!! Currently an (in) but will be fixed later. // backBufferFormat // fullscreen // pOptionalParams // Created 11/16/1999 johnstep //============================================================================= HRESULT CSwapChain::Reset( D3DPRESENT_PARAMETERS *pPresentationParameters ) { BOOL bDeviceLost = FALSE; HRESULT hr; // Validate First before changing state switch (pPresentationParameters->SwapEffect) { case D3DSWAPEFFECT_DISCARD: case D3DSWAPEFFECT_COPY: case D3DSWAPEFFECT_COPY_VSYNC: case D3DSWAPEFFECT_FLIP: case D3DSWAPEFFECT_NO_PRESENT: break; default: DPF_ERR("Invalid parameter for SwapEffect for D3DPRESENT_PARAMETERS. " "Must be one of D3DSWAPEFFECTS_COPY, D3DSWAPEFFECTS_COPY_VSYNC, " "D3DSWAPEFFECTS_DISCARD, or D3DSWAPEFFECTS_FLIP. CreateDevice/Reset Fails."); return D3DERR_INVALIDCALL; } if (pPresentationParameters->BackBufferCount) { if ((D3DSWAPEFFECT_COPY == pPresentationParameters->SwapEffect || D3DSWAPEFFECT_COPY_VSYNC == pPresentationParameters->SwapEffect) && (pPresentationParameters->BackBufferCount > 1)) { DPF_ERR("BackBufferCount must be 1 if SwapEffect is COPY/VSYNC. CreateDevice/Reset Fails."); pPresentationParameters->BackBufferCount = 1; return D3DERR_INVALIDCALL; } if (pPresentationParameters->BackBufferCount > D3DPRESENT_BACK_BUFFERS_MAX) { DPF_ERR("BackBufferCount must be less " "than D3DPRESENT_BACK_BUFFERS_MAX. CreateDevice/Reset Fails."); pPresentationParameters->BackBufferCount = D3DPRESENT_BACK_BUFFERS_MAX; return D3DERR_INVALIDCALL; } } else { pPresentationParameters->BackBufferCount = 1; DPF(4, "BackBufferCount not specified, considered default 1 "); } if (D3DSWAPEFFECT_DISCARD != pPresentationParameters->SwapEffect) { if (pPresentationParameters->MultiSampleType != D3DMULTISAMPLE_NONE) { DPF_ERR("Multisampling requires D3DSWAPEFFECT_DISCARD. CreateDevice/Reset Fails."); return D3DERR_INVALIDCALL; } } // D3DSWAPEFFECT_NO_PRESENT is a hack that only works for windowed mode if (D3DSWAPEFFECT_NO_PRESENT == pPresentationParameters->SwapEffect) { if (!pPresentationParameters->Windowed) { DPF_ERR("D3DSWAPEFFECT_NO_PRESENT only works when the device is windowed. CreateDevice/Reset Fails."); return D3DERR_INVALIDCALL; } } memcpy(&m_PresentationData, pPresentationParameters,sizeof m_PresentationData); // Remember the original swapeffect m_UserSwapEffect = pPresentationParameters->SwapEffect; // Convert discard to flip or copy based on stuff if (D3DSWAPEFFECT_DISCARD == pPresentationParameters->SwapEffect) { if (pPresentationParameters->Windowed && pPresentationParameters->BackBufferCount == 1) { m_PresentationData.SwapEffect = D3DSWAPEFFECT_COPY; } else { m_PresentationData.SwapEffect = D3DSWAPEFFECT_FLIP; } } if (NULL == m_PresentationData.hDeviceWindow) { m_PresentationData.hDeviceWindow= Device()->FocusWindow(); } DXGASSERT( NULL != m_PresentationData.hDeviceWindow); #ifdef WINNT // On NT, SetCooperativeLevel will fail if another device has exclusive // mode, so we cannot call it. On Win9X, it will not fail, but CreateSurface // WILL fail if we don't first call it, so we need to special case this call. if (m_UserSwapEffect != D3DSWAPEFFECT_NO_PRESENT) { #endif hr = SetCooperativeLevel(); if (FAILED(hr)) { DPF_ERR("SetCooperativeLevel returned failure. CreateDevice/Reset Failed"); return hr; } #ifdef WINNT } #endif // See if the device is lost if (D3D8IsDeviceLost(Device()->GetHandle())) { bDeviceLost = TRUE; FetchDirectDrawData(Device()->GetDeviceData(), Device()->GetInitFunction(), Device()->Enum()->GetUnknown16(Device()->AdapterIndex()), Device()->Enum()->GetHalOpList(Device()->AdapterIndex()), Device()->Enum()->GetNumHalOps(Device()->AdapterIndex())); } // Map the unknown format to a real one. If they will take any format // (i.e. the specified UNKNOWN), then we will try to give them the one // that matches the display format. if (m_PresentationData.Windowed) { // If we are windowed, we need to use the current display mode. We may be // able to relax this for new drivers. if (D3DFMT_UNKNOWN == m_PresentationData.BackBufferFormat) { m_PresentationData.BackBufferFormat = Device()->DisplayFormat(); } if (CPixel::SuppressAlphaChannel(m_PresentationData.BackBufferFormat) != Device()->DisplayFormat()) { DPF_ERR("Windowed BackBuffer Format must be compatible with Desktop Format. CreateDevice/Reset fails."); return D3DERR_INVALIDCALL; } } if (m_PresentationData.Windowed) { if ((m_PresentationData.BackBufferWidth < 1) || (m_PresentationData.BackBufferHeight < 1)) { RECT rc; if (GetClientRect(m_PresentationData.hDeviceWindow, &rc)) { if (m_PresentationData.BackBufferWidth < 1) m_PresentationData.BackBufferWidth = rc.right; if (m_PresentationData.BackBufferHeight < 1) m_PresentationData.BackBufferHeight = rc.bottom; } else { DPF_ERR("zero width and/or height and unable to get client. CreateDevice/Reset fails."); return D3DERR_INVALIDCALL; } } // We can handle color conversion from the back buffer if we use // GDI BitBlt instead of DirectDraw Blt for presentation. switch (m_PresentationData.BackBufferFormat) { case D3DFMT_X1R5G5B5: case D3DFMT_A1R5G5B5: case D3DFMT_R5G6B5: case D3DFMT_X8R8G8B8: case D3DFMT_A8R8G8B8: break; default: DPF_ERR("Unsupported back buffer format specified."); return D3DERR_INVALIDCALL; } // Does the device support offscreen RTs of this format in the current // display mode? if (FAILED(Device()->Enum()->CheckDeviceFormat( Device()->AdapterIndex(), Device()->GetDeviceType(), Device()->DisplayFormat(), D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, m_PresentationData.BackBufferFormat))) { DPF_ERR("This back buffer format is not supported for a windowed device. CreateDevice/Reset Fails"); DPF_ERR(" Use CheckDeviceType(Adapter, DeviceType, , , TRUE /* Windowed */)"); return D3DERR_INVALIDCALL; } // For now, always destroy existing surfaces and recreate. Later, we // may reuse the surfaces. We should also add an `initialized' flag, // but for now will just arbitrarily use m_pPrimarySurface for this // purpose. if (this == Device()->SwapChain()) { Device()->UpdateRenderTarget(NULL, NULL); if (m_pPrimarySurface != NULL) { Device()->ResourceManager()->DiscardBytes(0); static_cast(Device())->Destroy(); Destroy(); } if (Device()->GetZStencil() != NULL) { Device()->GetZStencil()->DecrementUseCount(); Device()->ResetZStencil(); } if (D3D8DoVidmemSurfacesExist(Device()->GetHandle())) { // user must free any video memory surfaces before doing // fullscreen Reset, otherwise we fail. DPF_ERR("All user created D3DPOOL_DEFAULT surfaces must be freed" " before Reset can succeed. Reset Fails."); return D3DERR_DEVICELOST; } } // If the device is lost, we should now restore it before creating // the new swap chain. if (bDeviceLost) { D3D8RestoreDevice(Device()->GetHandle()); } hr = CreateWindowed( Width(), Height(), BackBufferFormat(), m_PresentationData.BackBufferCount, m_PresentationData.MultiSampleType, (D3DSWAPEFFECT_DISCARD == m_UserSwapEffect), (pPresentationParameters->Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER) ); } else { D3DFORMAT FormatWithoutAlpha; #ifdef WINNT // Pick the best refresh rate m_PresentationData.FullScreen_RefreshRateInHz = PickRefreshRate( Width(), Height(), m_PresentationData.FullScreen_RefreshRateInHz, m_PresentationData.BackBufferFormat); #endif // If they specified a mode, does the mode exist? if (Width() != Device()->DisplayWidth() || Height() != Device()->DisplayHeight() || BackBufferFormat() != Device()->DisplayFormat() || ((m_PresentationData.FullScreen_RefreshRateInHz != 0) && (m_PresentationData.FullScreen_RefreshRateInHz != Device()->DisplayRate())) ) { D3DDISPLAYMODE* pModeTable = Device()->GetModeTable(); DWORD dwNumModes = Device()->GetNumModes(); DWORD i; #if DBG for (i = 0; i < dwNumModes; i++) { DPF(10,"Mode[%d] is %d x %d format=%08lx", i, pModeTable[i].Width, pModeTable[i].Height, pModeTable[i].Format); } #endif //DBG FormatWithoutAlpha = CPixel::SuppressAlphaChannel(m_PresentationData.BackBufferFormat); for (i = 0; i < dwNumModes; i++) { if ((pModeTable[i].Width == Width()) && (pModeTable[i].Height == Height()) && (pModeTable[i].Format == FormatWithoutAlpha)) { // So far so good. Check refresh rate if they specified one if ((m_PresentationData.FullScreen_RefreshRateInHz == 0) || (m_PresentationData.FullScreen_RefreshRateInHz == pModeTable[i].RefreshRate)) { break; } } } if (i == dwNumModes) { // The specified mode is invalid DPF_ERR("The specified mode is unsupported. CreateDevice/Reset Fails"); return D3DERR_INVALIDCALL; } // If the mode exists, does the device have caps in it? if (FAILED(Device()->Enum()->CheckDeviceType( Device()->AdapterIndex(), Device()->GetDeviceType(), FormatWithoutAlpha, m_PresentationData.BackBufferFormat, FALSE))) { DPF_ERR("Display Mode not supported by this device type. Use CheckDeviceType(X, X, ). CreateDevice/Reset Fails"); return D3DERR_INVALIDCALL; } // The mode is supported, so next we set the cooperative level to fullscreen // Now do the mode change and update the driver caps D3D8_SETMODEDATA SetModeData; SetModeData.hDD = Device()->GetHandle(); SetModeData.dwWidth = Width(); SetModeData.dwHeight = Height(); SetModeData.Format = BackBufferFormat(); SetModeData.dwRefreshRate = m_PresentationData.FullScreen_RefreshRateInHz; SetModeData.bRestore = FALSE; Device()->GetHalCallbacks()->SetMode(&SetModeData); if (SetModeData.ddRVal != DD_OK) { DPF_ERR("Unable to set the new mode. CreateDevice/Reset Fails"); return SetModeData.ddRVal; } FetchDirectDrawData(Device()->GetDeviceData(), Device()->GetInitFunction(), Device()->Enum()->GetUnknown16(Device()->AdapterIndex()), Device()->Enum()->GetHalOpList(Device()->AdapterIndex()), Device()->Enum()->GetNumHalOps(Device()->AdapterIndex())); // We have to restore the device now, since out mode change above would // have forced it to become lost. bDeviceLost = TRUE; // need to restore right away } // For now, always destroy existing surfaces and recreate. Later, we // may reuse the surfaces. We should also add an `initialized' flag, // but for now will just arbitrarily use m_pPrimarySurface for this // purpose. Device()->UpdateRenderTarget(NULL, NULL); if (m_pPrimarySurface != NULL) { Device()->ResourceManager()->DiscardBytes(0); static_cast(Device())->Destroy(); Destroy(); } if (Device()->GetZStencil() != NULL) { Device()->GetZStencil()->DecrementUseCount(); Device()->ResetZStencil(); } if (D3D8DoVidmemSurfacesExist(Device()->GetHandle())) { // user must free any video memory surfaces before doing // fullscreen Reset, otherwise we fail. DPF_ERR("All user created D3DPOOL_DEFAULT surfaces must be freed" " before Reset can succeed. Reset Fails"); return D3DERR_DEVICELOST; } if (bDeviceLost) { D3D8RestoreDevice(Device()->GetHandle()); } hr = CreateFullscreen( m_PresentationData.BackBufferWidth, m_PresentationData.BackBufferHeight, m_PresentationData.BackBufferFormat, m_PresentationData.BackBufferCount, m_PresentationData.FullScreen_PresentationInterval, m_PresentationData.MultiSampleType, (D3DSWAPEFFECT_DISCARD == m_UserSwapEffect), (pPresentationParameters->Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER) ); #ifdef WINNT if (SUCCEEDED(hr)) { MakeFullscreen(); } #endif //WINNT // Restore the gamma ramp if it was previously set if (m_GammaSet && SUCCEEDED(hr)) { SetGammaRamp(0, &m_DesiredGammaRamp); } } if (SUCCEEDED(hr)) { m_pCursor = new CCursor(Device()); m_bClientChanged = TRUE; m_pSrcRect = m_pDstRect = NULL; } return hr; } // Reset #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::ClipIntervals" //============================================================================= // ClipIntervals // // calculate [low1 high1] and [low2 high2] after considering [low high] // // - [low1 high1] will be the interval corresponding to the width/height of // target's size // // - [low2 high2] will be the interval corresponding to the width/height of // the source's size // // - [low high] will be the interval corresponding of the width/height of // the target clip // // The intention of this function is to clip the source for certain // stretch scenarios where the target is clipped. // // Created 05/17/2000 kanqiu //============================================================================= void ClipIntervals(long & low1, long & high1, long & low2, long & high2, const long low, const long high) { DXGASSERT(low1 < high1); DXGASSERT(low2 < high2); DXGASSERT(low < high); // shrink the target interval to lie within our Destination Clip [low high] if (low > low1) { low1 = low; } if (high < high1) { high1 = high; } // if the destination interval is the same size as the destination // clip, then we don't need to do anything long length1 = high1 - low1; long length = high - low; // see if clamp is needed for low2 and high2 proportionally if (length1 != length) { // find the length of our source interval long length2 = high2 - low2; // if the destination clip's low is outside our // target's low if (low < low1) { // Adjust the source low proportionally low2 += (low1 - low) * length2 / length; } // if the destination clip's high is outside our // target's high if (high > high1) { // Adjust the source high proportionally high2 -= (high - high1) * length2 / length; } /* * Check for zero-sized dimensions and bump if necessary */ DXGASSERT(high2 >= low2); if (low2 == high2) { if (low1 - low >= high - high1) { low2--; } else { high2++; } } } } // ClipIntervals #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::ClipRects" //============================================================================= // ClipRects // // calculate pSrc and pDst after considering pSrcRect and pDstRect // // Created 05/17/2000 kanqiu //============================================================================= inline HRESULT ClipRects(RECT * pSrc, RECT * pDst, RECT * pSrcRect, const RECT * pDstRect) { RECT SrcRect; if (pDstRect) { if (pDstRect->top >= pDst->bottom || pDstRect->bottom <= pDst->top || pDstRect->left >= pDst->right || pDstRect->right <= pDst->left || pDstRect->top >= pDstRect->bottom || pDstRect->left >= pDstRect->right ) { // in case of insane RECT, fail it DPF_ERR("Unable to present with invalid destionation RECT"); return D3DERR_INVALIDCALL; } if (pSrcRect) { SrcRect = *pSrcRect; pSrcRect = &SrcRect; //make a local copy and then update ClipIntervals(pDst->top,pDst->bottom,pSrcRect->top,pSrcRect->bottom, pDstRect->top,pDstRect->bottom); ClipIntervals(pDst->left,pDst->right,pSrcRect->left,pSrcRect->right, pDstRect->left,pDstRect->right); } else { ClipIntervals(pDst->top,pDst->bottom,pSrc->top,pSrc->bottom, pDstRect->top,pDstRect->bottom); ClipIntervals(pDst->left,pDst->right,pSrc->left,pSrc->right, pDstRect->left,pDstRect->right); } } // this pSrcRect is either what the user passed in (if there is no pDstRect) // or it now points to "SrcRect" temp which contains the clipped version // of what the user passed it. if (pSrcRect) { if (pSrcRect->top >= pSrc->bottom || pSrcRect->bottom <= pSrc->top || pSrcRect->left >= pSrc->right || pSrcRect->right <= pSrc->left || pSrcRect->top >= pSrcRect->bottom || pSrcRect->left >= pSrcRect->right ) { // in case of insane RECT, fail it DPF_ERR("Unable to present with invalid source RECT"); return D3DERR_INVALIDCALL; } ClipIntervals(pSrc->top,pSrc->bottom,pDst->top,pDst->bottom, pSrcRect->top,pSrcRect->bottom); ClipIntervals(pSrc->left,pSrc->right,pDst->left,pDst->right, pSrcRect->left,pSrcRect->right); } return S_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::UpdateFrameRate" /* * updateFrameRate */ void CSwapChain::UpdateFrameRate( void ) { /* * work out the frame rate if required... */ if( 0xffffffff == m_dwFlipTime ) { m_dwFlipTime = GetTickCount(); } m_dwFlipCnt++; if( m_dwFlipCnt >= 120 ) { DWORD time2; DWORD fps; char buff[256]; time2 = GetTickCount() - m_dwFlipTime; // Only do this at most every two seconds if (time2 >= 2000) { fps = (m_dwFlipCnt*10000)/time2; wsprintf(buff, "Adapter %d FPS = %ld.%01ld\r\n", Device()->AdapterIndex(), fps/10, fps % 10 ); OutputDebugString(buff); m_dwFlipTime = GetTickCount(); m_dwFlipCnt = 0; } } } /* updateFrameRate */ #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::DebugDiscardBackBuffer" #ifdef DEBUG void CSwapChain::DebugDiscardBackBuffer(HANDLE SurfaceToClear) const { // Disregard SW or Ref if (Device()->GetDeviceType() == D3DDEVTYPE_REF || Device()->GetDeviceType() == D3DDEVTYPE_SW) { return; } if (m_UserSwapEffect != D3DSWAPEFFECT_DISCARD) { return; } D3D8_BLTDATA ColorFill; ZeroMemory(&ColorFill, (sizeof ColorFill)); ColorFill.hDD = Device()->GetHandle(); ColorFill.hDestSurface = SurfaceToClear; ColorFill.dwFlags = DDBLT_COLORFILL | DDBLT_WAIT; ColorFill.rDest.right = Width(); ColorFill.rDest.bottom = Height(); // Switch between magenta and the inverse static BOOL bMagenta = FALSE; DWORD Color; switch(Device()->DisplayFormat()) { case D3DFMT_X8R8G8B8: case D3DFMT_R8G8B8: if (bMagenta) Color = 0x00FF007F; else Color = 0x0000FF00; break; case D3DFMT_X1R5G5B5: if (bMagenta) Color = 0x7C0F; else Color = 0x03E0; break; case D3DFMT_R5G6B5: if (bMagenta) Color = 0xF80F; else Color = 0x07E0; break; } if (bMagenta) bMagenta = FALSE; else bMagenta = TRUE; ColorFill.bltFX.dwFillColor = Color; // In debug we want to clear the back-buffer // if we're in discard mode Device()->GetHalCallbacks()->Blt(&ColorFill); return; } // DebugDiscardBackBuffer #endif // DEBUG #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::Present" //============================================================================= // IDirect3DSwapChain8::Present (public) // // Moves data from back-buffer to the primary // // Created 11/16/1999 johnstep //============================================================================= STDMETHODIMP CSwapChain::Present( CONST RECT *pSrcRect, CONST RECT *pDestRect, HWND hWndDestOverride, CONST RGNDATA *pDirtyRegion ) { API_ENTER(Device()); HRESULT hr = E_FAIL; // First, fail if the device is lost if (D3D8IsDeviceLost(Device()->GetHandle())) { return D3DERR_DEVICELOST; } if (!m_ppBackBuffers) { return D3DERR_DEVICELOST; } if (D3DSWAPEFFECT_FLIP == m_PresentationData.SwapEffect) { if (NULL != pSrcRect || NULL != pDestRect || NULL != pDirtyRegion) { DPF_ERR("pSrcRect pDestRect pDirtyRegion must be NULL with " "D3DSWAPEFFECT_FLIP. Present Fails."); return D3DERR_INVALIDCALL; } } if (NULL != pDirtyRegion) { DPF_ERR("Present with non-null pDirtyRegion is not supported"); return D3DERR_INVALIDCALL; } for (UINT i = 0; i < m_cBackBuffers; i++) { if (m_ppBackBuffers[i]->IsLocked()) { DPF_ERR("A BackBuffer in this swap chain is Locked. Present failed."); return D3DERR_INVALIDCALL; } } // Check if we need to act against HW that queues too much if (PresentUseBlt()) { if (Device()->GetDeviceData()->DriverData.D3DCaps.MaxStreams == 0) { // Only pre-DX8 level drivers are suspected... if (0 == (Device()->GetDeviceData()->DriverData.KnownDriverFlags & KNOWN_NOTAWINDOWEDBLTQUEUER)) { // We don't want to treat a vis-region change as a failure to // prevent the thunk layer from calling Reset. Reset // confuses the clip-list caching that is done for Present. // // Also we want don't want to spew any errors here. DPF_MUTE(); D3DLOCKED_RECT LockRect; // all we need is a Lock sent down to driver so it would flush the queue // therefore 1x1 rect is enough, larger area of lock would cause sprites to flick // and therefore also slow down the system. RECT DummyRect={0,0,1,1}; hr = m_pPrimarySurface->InternalLockRect(&LockRect, &DummyRect, DDLOCK_FAILONVISRGNCHANGED); if (SUCCEEDED(hr)) { m_pPrimarySurface->InternalUnlockRect(); } else { hr = S_OK; } DPF_UNMUTE(); } } } #ifdef WINNT // If ~ 50 seconds have passed (assuming a 10Hz flip rate) // and this is a primary surface, then make a magic call to // disable screen savers. // This isn't needed on 9x since we make a SPI call on that OS // to disable screen savers. if (0 == (Device()->BehaviorFlags() & 0x10000000)) //SCREENSAVER magic number { if (!m_PresentationData.Windowed) { static DWORD dwMagicTime = 0; dwMagicTime++; if (dwMagicTime > (50*10) ) { DWORD dw=60*15; dwMagicTime = 0; SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT,0,&dw,0); SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT,dw,0,0); } } } #endif // Flush any pending commands before we send the Flip/Blt static_cast(Device())->FlushStatesNoThrow(); if ( FALSE == PresentUseBlt()) { // We are fullscreen, so turn this into a flip DXGASSERT(0==m_presentnext); hr = FlipToSurface(BackBuffer(0)->KernelHandle()); } else { if (m_PresentationData.Windowed) { RECT DestRect; // // Choose the presentation window. Override, or device window? // if (hWndDestOverride) m_BltData.hWnd = hWndDestOverride; else m_BltData.hWnd = m_PresentationData.hDeviceWindow; //The left and top members are zero. The right and bottom //members contain the width and height of the window. if (!GetClientRect(m_BltData.hWnd, &DestRect)) { // in case of this unlikely event, fail it DPF_ERR("Unable to get client during presentation"); return D3DERR_INVALIDCALL; } if (((UINT)DestRect.bottom != m_ClientHeight) || ((UINT)DestRect.right != m_ClientWidth) ) { m_bClientChanged = TRUE; m_ClientHeight = (UINT)DestRect.bottom; m_ClientWidth = (UINT)DestRect.right; } } if (D3DSWAPEFFECT_FLIP != m_PresentationData.SwapEffect) { if (pSrcRect) { if (m_pSrcRect) { if (memcmp(pSrcRect,m_pSrcRect,sizeof RECT)) { m_bClientChanged = TRUE; m_SrcRect = *pSrcRect; } } else { m_bClientChanged = TRUE; m_pSrcRect = &m_SrcRect; m_SrcRect = *pSrcRect; } } else if (m_pSrcRect) { m_bClientChanged = TRUE; m_pSrcRect = NULL; } if (pDestRect) { if (m_pDstRect) { if (memcmp(pDestRect,m_pDstRect,sizeof RECT)) { m_bClientChanged = TRUE; m_DstRect = *pDestRect; } } else { m_bClientChanged = TRUE; m_pDstRect = &m_DstRect; m_DstRect = *pDestRect; } } else if (m_pDstRect) { m_bClientChanged = TRUE; m_pDstRect = NULL; } } if (m_bClientChanged) { m_bClientChanged = FALSE; m_BltData.rSrc.left = m_BltData.rSrc.top = 0; m_BltData.rSrc.right = Width(); m_BltData.rSrc.bottom = Height(); m_BltData.rDest.left = m_BltData.rDest.top = 0; m_BltData.rDest.right = m_ClientWidth; m_BltData.rDest.bottom = m_ClientHeight; hr = ClipRects((RECT*)&m_BltData.rSrc, (RECT*)&m_BltData.rDest, m_pSrcRect, m_pDstRect); if (FAILED(hr)) { return hr; } } m_BltData.hSrcSurface = m_ppBackBuffers[m_presentnext]->KernelHandle(); // Lock the software driver created buffer // and unlock it immediately if ((D3DDEVTYPE_HAL != Device()->GetDeviceType()) && (D3DMULTISAMPLE_NONE != m_PresentationData.MultiSampleType) ) { D3D8_LOCKDATA lockData; ZeroMemory(&lockData, sizeof lockData); lockData.hDD = Device()->GetHandle(); lockData.hSurface = m_BltData.hSrcSurface; lockData.dwFlags = DDLOCK_READONLY; hr = Device()->GetHalCallbacks()->Lock(&lockData); if (SUCCEEDED(hr)) { D3D8_UNLOCKDATA unlockData; ZeroMemory(&unlockData, sizeof unlockData); unlockData.hDD = Device()->GetHandle(); unlockData.hSurface = m_BltData.hSrcSurface; hr = Device()->GetHalCallbacks()->Unlock(&unlockData); if (FAILED(hr)) { DPF_ERR("Driver failed to unlock MultiSample backbuffer. Present fails."); return hr; } } else { DPF_ERR("Driver failed to lock MultiSample backbuffer. Present Fails."); return hr; } } if (DDHAL_DRIVER_NOTHANDLED == Device()->GetHalCallbacks()->Blt(&m_BltData)) { hr = E_FAIL; } else { hr = m_BltData.ddRVal; // Handle deferred DP2 errors specially if (hr == D3DERR_DEFERRED_DP2ERROR) { // We only want to make this "error" visible // if we have been created with the right flag if (Device()->BehaviorFlags() & D3DCREATE_SHOW_DP2ERROR) { DPF_ERR("A prior call to DrawPrim2 has failed; returning error from Present."); } else { // Quietly just mask this error; this is ok; because // we known that the Blt succeeded hr = S_OK; } } } if (FAILED(hr)) { DPF_ERR("BitBlt or StretchBlt failed in Present"); return hr; } // Clear the backbuffer if the user has specified // discard semantics DebugDiscardBackBuffer(m_BltData.hSrcSurface); if (m_pMirrorSurface) { hr = FlipToSurface(m_pMirrorSurface->KernelHandle()); // need to reset it m_BltData.hDestSurface = m_pMirrorSurface->KernelHandle(); if (FAILED(hr)) { DPF_ERR("Driver failed Flip. Present Fails."); return hr; } } if (m_cBackBuffers > 1) { if (m_PresentationData.SwapEffect == D3DSWAPEFFECT_FLIP) { HANDLE hRenderTargetHandle = Device()->RenderTarget()->KernelHandle(); BOOL bNeedSetRendertarget = FALSE; HANDLE hSurfTarg = BackBuffer(0)->KernelHandle(); DXGASSERT(0 == m_presentnext); for (int i = m_cBackBuffers - 1; i >= 0; i--) { if (hSurfTarg == hRenderTargetHandle) bNeedSetRendertarget = TRUE; // This swap handles function will // return the value that were currently // in the surface; which we use to // pass to the next surface. m_ppBackBuffers[i]->SwapKernelHandles(&hSurfTarg); } if (bNeedSetRendertarget) (static_cast(Device()))->SetRenderTargetI( Device()->RenderTarget(), Device()->ZBuffer()); } else if (++m_presentnext >= m_cBackBuffers) { m_presentnext = 0; } } } if ( D3D_REGFLAGS_SHOWFRAMERATE & m_dwFlags) { UpdateFrameRate(); } return hr; } // Present HRESULT CSwapChain::FlipToSurface(HANDLE hTargetSurface) { HRESULT hr; D3D8_FLIPDATA FlipData; HANDLE hSurfTarg; FlipData.hDD = Device()->GetHandle(); FlipData.hSurfCurr = PrimarySurface()->KernelHandle(); FlipData.hSurfTarg = hTargetSurface; FlipData.hSurfCurrLeft = NULL; FlipData.hSurfTargLeft = NULL; FlipData.dwFlags = m_dwFlipFlags; m_pCursor->Flip(); hr = m_pCursor->Show(FlipData.hSurfTarg); Device()->GetHalCallbacks()->Flip(&FlipData); m_pCursor->Flip(); hr = m_pCursor->Hide(FlipData.hSurfCurr); m_pCursor->Flip(); hr = FlipData.ddRVal; // Handle deferred DP2 errors specially if (hr == D3DERR_DEFERRED_DP2ERROR) { // We only want to make this "error" visible // if we have been created with the right flag if (Device()->BehaviorFlags() & D3DCREATE_SHOW_DP2ERROR) { DPF_ERR("A prior call to DrawPrim2 has failed; returning error from Present."); } else { // Quietly just mask this error; this is ok; because // we known that the Flip succeeded hr = S_OK; } } // In debug, we may need to clear the data from // our new back-buffer if the user specified // SWAPEFFECT_DISCARD DebugDiscardBackBuffer(FlipData.hSurfCurr); if (m_pMirrorSurface) { hSurfTarg = PrimarySurface()->KernelHandle(); m_pMirrorSurface->SwapKernelHandles(&hSurfTarg); PrimarySurface()->SwapKernelHandles(&hSurfTarg); } else { HANDLE hRenderTargetHandle; CBaseSurface* pRenderTarget = Device()->RenderTarget(); if (pRenderTarget) hRenderTargetHandle = pRenderTarget->KernelHandle(); else hRenderTargetHandle = 0; while (hTargetSurface != PrimarySurface()->KernelHandle()) { hSurfTarg = PrimarySurface()->KernelHandle(); for (int i = m_cBackBuffers-1; i>=0; i--) { BackBuffer(i)->SwapKernelHandles(&hSurfTarg); } PrimarySurface()->SwapKernelHandles(&hSurfTarg); } if (hRenderTargetHandle) { BOOL bNeedSetRendertarget; if (PrimarySurface()->KernelHandle() == hRenderTargetHandle) { bNeedSetRendertarget = TRUE; } else { bNeedSetRendertarget = FALSE; for (int i = m_cBackBuffers-1; i>=0; i--) { if (BackBuffer(i)->KernelHandle() == hRenderTargetHandle) { bNeedSetRendertarget = TRUE; break; } } } if (bNeedSetRendertarget) { (static_cast(Device()))->SetRenderTargetI( Device()->RenderTarget(), Device()->ZBuffer()); } } } return hr; } HRESULT CSwapChain::FlipToGDISurface(void) { D3D8_FLIPTOGDISURFACEDATA FlipToGDISurfaceData; FlipToGDISurfaceData.ddRVal = DD_OK; FlipToGDISurfaceData.dwToGDI = TRUE; FlipToGDISurfaceData.hDD = Device()->GetHandle(); Device()->GetHalCallbacks()->FlipToGDISurface(&FlipToGDISurfaceData); if (NULL != m_hGDISurface && PrimarySurface() && PrimarySurface()->KernelHandle() != m_hGDISurface) { return FlipToSurface(m_hGDISurface); } return FlipToGDISurfaceData.ddRVal; } // FlipToGDISurface void CSwapChain::SetGammaRamp( DWORD dwFlags, // Calibrated or not. CONST D3DGAMMARAMP *pRamp) { D3DGAMMARAMP TempRamp; D3DGAMMARAMP * pRampToPassToHardware; m_DesiredGammaRamp = *pRamp; // Assume this for now. Calibration may use a temporary. pRampToPassToHardware = &m_DesiredGammaRamp; // If they want to calibrate the gamma, we will do that now. We will // copy this to a different buffer so that we don't mess up the one // passed in to us. if (dwFlags & D3DSGR_CALIBRATE) { TempRamp = *pRamp; Device()->Enum()->LoadAndCallGammaCalibrator( &TempRamp, (UCHAR*) Device()->GetDeviceData()->DriverName); pRampToPassToHardware = &TempRamp; } DXGASSERT(pRampToPassToHardware); DXGASSERT(Device()->GetDeviceData()->hDD); DXGASSERT(Device()->GetDeviceData()->hDC); D3D8SetGammaRamp( Device()->GetDeviceData()->hDD, Device()->GetDeviceData()->hDC, pRampToPassToHardware); if (pRamp != NULL) { m_GammaSet = TRUE; } else { m_GammaSet = FALSE; } } void CSwapChain::GetGammaRamp( D3DGAMMARAMP *pRamp) { *pRamp = m_DesiredGammaRamp; } #undef DPF_MODNAME #define DPF_MODNAME "CSwapChain::SetCooperativeLevel" /* * DD_SetCooperativeLevel */ HRESULT CSwapChain::SetCooperativeLevel() { #if _WIN32_WINNT >= 0x0501 { //Turn off ghosting for any exclusive-mode app //(Whistler onwards only) typedef void (WINAPI *PFN_NOGHOST)( void ); HINSTANCE hInst = NULL; hInst = LoadLibrary( "user32.dll" ); if( hInst ) { PFN_NOGHOST pfnNoGhost = NULL; pfnNoGhost = (PFN_NOGHOST)GetProcAddress( (HMODULE)hInst, "DisableProcessWindowsGhosting" ); if( pfnNoGhost ) { pfnNoGhost(); } FreeLibrary( hInst ); } } #endif // _WIN32_WINNT >= 0x0501 HRESULT ddrval; #ifndef WINNT ddrval = D3D8SetCooperativeLevel(Device()->GetHandle(), m_PresentationData.hDeviceWindow, m_PresentationData.Windowed ? DDSCL_NORMAL : (DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_SETDEVICEWINDOW)); if (FAILED(ddrval)) return ddrval; #else BOOL bThisDeviceOwnsExclusive; BOOL bExclusiveExists; bExclusiveExists = Device()->Enum()->CheckExclusiveMode(Device(), &bThisDeviceOwnsExclusive, !m_PresentationData.Windowed); /* * exclusive mode? */ if (m_PresentationData.Windowed) /* * no, must be regular */ { DoneExclusiveMode(FALSE); ddrval = SetAppHWnd(); } if (bExclusiveExists && !bThisDeviceOwnsExclusive) { DPF_ERR("Exclusive Mode has been taken by other app or " "other device on the same adapter. " "SetCooperativeLevel returns D3DERR_DEVICELOST."); return D3DERR_DEVICELOST; } if (!m_PresentationData.Windowed) { if (GetWindowLong(Device()->FocusWindow(), GWL_STYLE) & WS_CHILD) { DPF_ERR( "Focus Window must be a top level window. CreateDevice fails." ); return D3DERR_INVALIDCALL; } ddrval = SetAppHWnd(); if (S_OK == ddrval) { StartExclusiveMode(FALSE); SetForegroundWindow(m_PresentationData.hDeviceWindow); } } #endif return ddrval; } /* SetCooperativeLevel */ #ifdef WINNT /* * PickRefreshRate * * On NT, we want to pick a high reffresh rate, but we don't want to pick one * too high. In theory, mode pruning would be 100% safe and we can always pick * a high one, but we don't trust it 100%. */ DWORD CSwapChain::PickRefreshRate( DWORD Width, DWORD Height, DWORD RefreshRate, D3DFORMAT Format) { D3DFORMAT FormatWithoutAlpha; D3DDISPLAYMODE* pModeTable = Device()->GetModeTable(); DWORD dwNumModes = Device()->GetNumModes(); DWORD i; FormatWithoutAlpha = CPixel::SuppressAlphaChannel(Format); // We will always use the refresh rate from the registry if it's specified. if (m_dwForceRefreshRate > 0) { for (i = 0; i < dwNumModes; i++) { if ((pModeTable[i].Width == Width) && (pModeTable[i].Height == Height) && (pModeTable[i].Format == FormatWithoutAlpha) && (m_dwForceRefreshRate == pModeTable[i].RefreshRate)) { return m_dwForceRefreshRate; } } } // If the app specified the refresh rate, then we'll use it; otherwise, we // will pick one ourselves. if (RefreshRate == 0) { // If the mode requires no more bandwidth than the desktop mode from which // the app was launched, we will go ahead and try that mode. DEVMODE dm; ZeroMemory(&dm, sizeof dm); dm.dmSize = sizeof dm; EnumDisplaySettings(Device()->GetDeviceData()->DriverName, ENUM_REGISTRY_SETTINGS, &dm); if ((Width <= dm.dmPelsWidth) && (Height <= dm.dmPelsHeight)) { // Now check to see if it's supported for (i = 0; i < dwNumModes; i++) { if ((pModeTable[i].Width == Width) && (pModeTable[i].Height == Height) && (pModeTable[i].Format == FormatWithoutAlpha) && (dm.dmDisplayFrequency == pModeTable[i].RefreshRate)) { RefreshRate = dm.dmDisplayFrequency; break; } } } // If we still don't have a refresh rate, try 75hz if (RefreshRate == 0) { for (i = 0; i < dwNumModes; i++) { if ((pModeTable[i].Width == Width) && (pModeTable[i].Height == Height) && (pModeTable[i].Format == FormatWithoutAlpha) && (75 == pModeTable[i].RefreshRate)) { RefreshRate = 75; break; } } } // If we still don't have a refresh rate, use 60hz if (RefreshRate == 0) { for (i = 0; i < dwNumModes; i++) { if ((pModeTable[i].Width == Width) && (pModeTable[i].Height == Height) && (pModeTable[i].Format == FormatWithoutAlpha) && (pModeTable[i].RefreshRate == 60)) { RefreshRate = pModeTable[i].RefreshRate; break; } } } } return RefreshRate; } #endif // End of file : swapchain.cpp