Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2316 lines
73 KiB

//=============================================================================
// 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<void *>(
static_cast<IDirect3DSwapChain8 *>(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<WORD>(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, <Current Display Format>, <Desired BackBufferFormat>, 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<CD3DBase*>(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, <Desired fullscreen format>). 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<CD3DBase*>(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<CD3DBase*>(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<CD3DBase*>(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<CD3DBase*>(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