/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2006 Torus Knot Software Ltd Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. You may alternatively use this source under the terms of a specific version of the OGRE Unrestricted License provided you have obtained such a license from Torus Knot Software Ltd. ----------------------------------------------------------------------------- */ #include "OgreD3D9RenderWindow.h" #include "OgreLogManager.h" #include "OgreViewport.h" #include "OgreException.h" #include "OgreD3D9RenderSystem.h" #include "OgreRenderSystem.h" #include "OgreBitwise.h" #include "OgreImageCodec.h" #include "OgreStringConverter.h" #include "OgreNoMemoryMacros.h" #include #include "OgreMemoryMacros.h" #include "OgreRoot.h" #include "OgreWindowEventUtilities.h" namespace Ogre { D3D9RenderWindow::D3D9RenderWindow(HINSTANCE instance, D3D9Driver *driver, LPDIRECT3DDEVICE9 deviceIfSwapChain) : mInstance(instance) , mDriver(driver) , mpRenderSurface(0) { mIsFullScreen = false; mIsSwapChain = (deviceIfSwapChain != NULL); mIsExternal = false; mHWnd = 0; mActive = false; mSizing = false; mClosed = false; mSwitchingFullscreen = false; mDisplayFrequency = 0; } D3D9RenderWindow::~D3D9RenderWindow() { destroy(); } bool D3D9RenderWindow::_checkMultiSampleQuality(D3DMULTISAMPLE_TYPE type, DWORD *outQuality, D3DFORMAT format, UINT adapterNum, D3DDEVTYPE deviceType, BOOL fullScreen) { LPDIRECT3D9 pD3D = mDriver->getD3D(); if (SUCCEEDED(pD3D->CheckDeviceMultiSampleType( adapterNum, deviceType, format, fullScreen, type, outQuality))) return true; else return false; } void D3D9RenderWindow::create(const String& name, unsigned int width, unsigned int height, bool fullScreen, const NameValuePairList *miscParams) { HINSTANCE hInst = mInstance; D3D9Driver* driver = mDriver; HWND parentHWnd = 0; HWND externalHandle = 0; mFSAAType = D3DMULTISAMPLE_NONE; mFSAAQuality = 0; mVSync = false; String title = name; unsigned int colourDepth = 32; int left = -1; // Defaults to screen center int top = -1; // Defaults to screen center bool depthBuffer = true; String border = ""; bool outerSize = false; mUseNVPerfHUD = false; if(miscParams) { // Get variable-length params NameValuePairList::const_iterator opt; // left (x) opt = miscParams->find("left"); if(opt != miscParams->end()) left = StringConverter::parseInt(opt->second); // top (y) opt = miscParams->find("top"); if(opt != miscParams->end()) top = StringConverter::parseInt(opt->second); // Window title opt = miscParams->find("title"); if(opt != miscParams->end()) title = opt->second; // parentWindowHandle -> parentHWnd opt = miscParams->find("parentWindowHandle"); if(opt != miscParams->end()) parentHWnd = (HWND)StringConverter::parseUnsignedInt(opt->second); // externalWindowHandle -> externalHandle opt = miscParams->find("externalWindowHandle"); if(opt != miscParams->end()) externalHandle = (HWND)StringConverter::parseUnsignedInt(opt->second); // vsync [parseBool] opt = miscParams->find("vsync"); if(opt != miscParams->end()) mVSync = StringConverter::parseBool(opt->second); // displayFrequency opt = miscParams->find("displayFrequency"); if(opt != miscParams->end()) mDisplayFrequency = StringConverter::parseUnsignedInt(opt->second); // colourDepth opt = miscParams->find("colourDepth"); if(opt != miscParams->end()) colourDepth = StringConverter::parseUnsignedInt(opt->second); // depthBuffer [parseBool] opt = miscParams->find("depthBuffer"); if(opt != miscParams->end()) depthBuffer = StringConverter::parseBool(opt->second); // FSAA type opt = miscParams->find("FSAA"); if(opt != miscParams->end()) mFSAAType = (D3DMULTISAMPLE_TYPE)StringConverter::parseUnsignedInt(opt->second); // FSAA quality opt = miscParams->find("FSAAQuality"); if(opt != miscParams->end()) mFSAAQuality = StringConverter::parseUnsignedInt(opt->second); // window border style opt = miscParams->find("border"); if(opt != miscParams->end()) border = opt->second; // set outer dimensions? opt = miscParams->find("outerDimensions"); if(opt != miscParams->end()) outerSize = StringConverter::parseBool(opt->second); // NV perf HUD? opt = miscParams->find("useNVPerfHUD"); if(opt != miscParams->end()) mUseNVPerfHUD = StringConverter::parseBool(opt->second); } // Destroy current window if any if( mHWnd ) destroy(); if (!externalHandle) { DWORD dwStyle = WS_VISIBLE | WS_CLIPCHILDREN; RECT rc; mWidth = width; mHeight = height; mTop = top; mLeft = left; if (!fullScreen) { if (parentHWnd) { dwStyle |= WS_CHILD; } else { if (border == "none") dwStyle |= WS_POPUP; else if (border == "fixed") dwStyle |= WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; else dwStyle |= WS_OVERLAPPEDWINDOW; } if (!outerSize) { // Calculate window dimensions required // to get the requested client area SetRect(&rc, 0, 0, mWidth, mHeight); AdjustWindowRect(&rc, dwStyle, false); mWidth = rc.right - rc.left; mHeight = rc.bottom - rc.top; // Clamp width and height to the desktop dimensions int screenw = GetSystemMetrics(SM_CXSCREEN); int screenh = GetSystemMetrics(SM_CYSCREEN); if ((int)mWidth > screenw) mWidth = screenw; if ((int)mHeight > screenh) mHeight = screenh; if (mLeft < 0) mLeft = (screenw - mWidth) / 2; if (mTop < 0) mTop = (screenh - mHeight) / 2; } } else { dwStyle |= WS_POPUP; mTop = mLeft = 0; } // Register the window class // NB allow 4 bytes of window data for D3D9RenderWindow pointer WNDCLASS wc = { 0, WindowEventUtilities::_WndProc, 0, 0, hInst, LoadIcon(0, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH), 0, "OgreD3D9Wnd" }; RegisterClass(&wc); // Create our main window // Pass pointer to self mIsExternal = false; mHWnd = CreateWindow("OgreD3D9Wnd", title.c_str(), dwStyle, mLeft, mTop, mWidth, mHeight, parentHWnd, 0, hInst, this); WindowEventUtilities::_addRenderWindow(this); } else { mHWnd = externalHandle; mIsExternal = true; } RECT rc; // top and left represent outer window coordinates GetWindowRect(mHWnd, &rc); mTop = rc.top; mLeft = rc.left; // width and height represent interior drawable area GetClientRect(mHWnd, &rc); mWidth = rc.right; mHeight = rc.bottom; mName = name; mIsDepthBuffered = depthBuffer; mIsFullScreen = fullScreen; mColourDepth = colourDepth; StringUtil::StrStreamType str; str << "D3D9 : Created D3D9 Rendering Window '" << mName << "' : " << mWidth << "x" << mHeight << ", " << mColourDepth << "bpp"; LogManager::getSingleton().logMessage( LML_NORMAL, str.str()); createD3DResources(); mActive = true; mClosed = false; } void D3D9RenderWindow::setFullscreen(bool fullScreen, unsigned int width, unsigned int height) { if (fullScreen != mIsFullScreen || width != mWidth || height != mHeight) { if (fullScreen != mIsFullScreen) mSwitchingFullscreen = true; DWORD dwStyle = WS_VISIBLE | WS_CLIPCHILDREN; bool oldFullscreen = mIsFullScreen; mIsFullScreen = fullScreen; if (fullScreen) { dwStyle |= WS_POPUP; mTop = mLeft = 0; mWidth = width; mHeight = height; // need different ordering here if (oldFullscreen) { // was previously fullscreen, just changing the resolution SetWindowPos(mHWnd, HWND_TOPMOST, 0, 0, width, height, SWP_NOACTIVATE); } else { SetWindowPos(mHWnd, HWND_TOPMOST, 0, 0, width, height, SWP_NOACTIVATE); //MoveWindow(mHWnd, mLeft, mTop, mWidth, mHeight, FALSE); SetWindowLong(mHWnd, GWL_STYLE, dwStyle); SetWindowPos(mHWnd, 0, 0,0, 0,0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); } } else { dwStyle |= WS_OVERLAPPEDWINDOW; // Calculate window dimensions required // to get the requested client area RECT rc; SetRect(&rc, 0, 0, width, height); AdjustWindowRect(&rc, dwStyle, false); unsigned int winWidth = rc.right - rc.left; unsigned int winHeight = rc.bottom - rc.top; SetWindowLong(mHWnd, GWL_STYLE, dwStyle); SetWindowPos(mHWnd, HWND_NOTOPMOST, 0, 0, winWidth, winHeight, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOACTIVATE); // Note that we also set the position in the restoreLostDevice method // via _finishSwitchingFullScreen } md3dpp.Windowed = !fullScreen; md3dpp.FullScreen_RefreshRateInHz = mIsFullScreen ? mDisplayFrequency : 0; md3dpp.BackBufferHeight = height; md3dpp.BackBufferWidth = width; if ((oldFullscreen && fullScreen) || mIsExternal) { // Have to release & trigger device reset // NB don't use windowMovedOrResized since Win32 doesn't know // about the size change yet SAFE_RELEASE(mpRenderSurface); static_cast(Root::getSingleton().getRenderSystem())->_notifyDeviceLost(); // Notify viewports of resize ViewportList::iterator it = mViewportList.begin(); while(it != mViewportList.end()) (*it++).second->_updateDimensions(); } } } void D3D9RenderWindow::_finishSwitchingFullscreen() { if(mIsFullScreen) { // Need to reset the region on the window sometimes, when the // windowed mode was constrained by desktop HRGN hRgn = CreateRectRgn(0,0,md3dpp.BackBufferWidth, md3dpp.BackBufferHeight); SetWindowRgn(mHWnd, hRgn, FALSE); } else { // When switching back to windowed mode, need to reset window size // after device has been restored RECT rc; SetRect(&rc, 0, 0, md3dpp.BackBufferWidth, md3dpp.BackBufferHeight); AdjustWindowRect(&rc, GetWindowLong(mHWnd, GWL_STYLE), false); unsigned int winWidth = rc.right - rc.left; unsigned int winHeight = rc.bottom - rc.top; int screenw = GetSystemMetrics(SM_CXSCREEN); int screenh = GetSystemMetrics(SM_CYSCREEN); int left = (screenw - winWidth) / 2; int top = (screenh - winHeight) / 2; SetWindowPos(mHWnd, HWND_NOTOPMOST, left, top, winWidth, winHeight, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOACTIVATE); } mSwitchingFullscreen = false; } void D3D9RenderWindow::createD3DResources(void) { // access device via driver LPDIRECT3DDEVICE9 mpD3DDevice = mDriver->getD3DDevice(); if (mIsSwapChain && !mpD3DDevice) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Secondary window has not been given the device from the primary!", "D3D9RenderWindow::createD3DResources"); } SAFE_RELEASE(mpRenderSurface); // Set up the presentation parameters HRESULT hr; LPDIRECT3D9 pD3D = mDriver->getD3D(); D3DDEVTYPE devType = D3DDEVTYPE_HAL; ZeroMemory( &md3dpp, sizeof(D3DPRESENT_PARAMETERS) ); md3dpp.Windowed = !mIsFullScreen; md3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // triple buffer if VSync is on md3dpp.BackBufferCount = mVSync ? 2 : 1; md3dpp.EnableAutoDepthStencil = mIsDepthBuffered; md3dpp.hDeviceWindow = mHWnd; md3dpp.BackBufferWidth = mWidth; md3dpp.BackBufferHeight = mHeight; md3dpp.FullScreen_RefreshRateInHz = mIsFullScreen ? mDisplayFrequency : 0; if (mVSync) { md3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; } else { // NB not using vsync in windowed mode in D3D9 can cause jerking at low // frame rates no matter what buffering modes are used (odd - perhaps a // timer issue in D3D9 since GL doesn't suffer from this) // low is < 200fps in this context if (!mIsFullScreen) { LogManager::getSingleton().logMessage("D3D9 : WARNING - " "disabling VSync in windowed mode can cause timing issues at lower " "frame rates, turn VSync on if you observe this problem."); } md3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; } md3dpp.BackBufferFormat = D3DFMT_R5G6B5; if( mColourDepth > 16 ) md3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; if (mColourDepth > 16 ) { // Try to create a 32-bit depth, 8-bit stencil if( FAILED( pD3D->CheckDeviceFormat(mDriver->getAdapterNumber(), devType, md3dpp.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24S8 ))) { // Bugger, no 8-bit hardware stencil, just try 32-bit zbuffer if( FAILED( pD3D->CheckDeviceFormat(mDriver->getAdapterNumber(), devType, md3dpp.BackBufferFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D32 ))) { // Jeez, what a naff card. Fall back on 16-bit depth buffering md3dpp.AutoDepthStencilFormat = D3DFMT_D16; } else md3dpp.AutoDepthStencilFormat = D3DFMT_D32; } else { // Woohoo! if( SUCCEEDED( pD3D->CheckDepthStencilMatch( mDriver->getAdapterNumber(), devType, md3dpp.BackBufferFormat, md3dpp.BackBufferFormat, D3DFMT_D24S8 ) ) ) { md3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; } else md3dpp.AutoDepthStencilFormat = D3DFMT_D24X8; } } else // 16-bit depth, software stencil md3dpp.AutoDepthStencilFormat = D3DFMT_D16; md3dpp.MultiSampleType = mFSAAType; md3dpp.MultiSampleQuality = (mFSAAQuality == 0) ? 0 : mFSAAQuality; if (mIsSwapChain) { // Create swap chain hr = mpD3DDevice->CreateAdditionalSwapChain( &md3dpp, &mpSwapChain); if (FAILED(hr)) { // Try a second time, may fail the first time due to back buffer count, // which will be corrected by the runtime hr = mpD3DDevice->CreateAdditionalSwapChain( &md3dpp, &mpSwapChain); } if (FAILED(hr)) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Unable to create an additional swap chain", "D3D9RenderWindow::createD3DResources"); } // Store references to buffers for convenience mpSwapChain->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &mpRenderSurface ); // Additional swap chains need their own depth buffer // to support resizing them if (mIsDepthBuffered) { hr = mpD3DDevice->CreateDepthStencilSurface( mWidth, mHeight, md3dpp.AutoDepthStencilFormat, md3dpp.MultiSampleType, md3dpp.MultiSampleQuality, (md3dpp.Flags & D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL), &mpRenderZBuffer, NULL ); if (FAILED(hr)) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Unable to create a depth buffer for the swap chain", "D3D9RenderWindow::createD3DResources"); } } else { mpRenderZBuffer = 0; } } else { if (!mpD3DDevice) { // We haven't created the device yet, this must be the first time // Do we want to preserve the FPU mode? Might be useful for scientific apps DWORD extraFlags = 0; ConfigOptionMap& options = Root::getSingleton().getRenderSystem()->getConfigOptions(); ConfigOptionMap::iterator opti = options.find("Floating-point mode"); if (opti != options.end() && opti->second.currentValue == "Consistent") extraFlags |= D3DCREATE_FPU_PRESERVE; #if OGRE_THREAD_SUPPORT extraFlags |= D3DCREATE_MULTITHREADED; #endif // Set default settings (use the one Ogre discovered as a default) UINT adapterToUse = mDriver->getAdapterNumber(); if (mUseNVPerfHUD) { // Look for 'NVIDIA NVPerfHUD' adapter (<= v4) // or 'NVIDIA PerfHUD' (v5) // If it is present, override default settings for (UINT adapter=0; adapter < mDriver->getD3D()->GetAdapterCount(); ++adapter) { D3DADAPTER_IDENTIFIER9 identifier; HRESULT res; res = mDriver->getD3D()->GetAdapterIdentifier(adapter,0,&identifier); if (strstr(identifier.Description,"PerfHUD") != 0) { adapterToUse = adapter; devType = D3DDEVTYPE_REF; break; } } } hr = pD3D->CreateDevice(adapterToUse, devType, mHWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING | extraFlags, &md3dpp, &mpD3DDevice ); if (FAILED(hr)) { // Try a second time, may fail the first time due to back buffer count, // which will be corrected down to 1 by the runtime hr = pD3D->CreateDevice( adapterToUse, devType, mHWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING | extraFlags, &md3dpp, &mpD3DDevice ); } if( FAILED( hr ) ) { hr = pD3D->CreateDevice( adapterToUse, devType, mHWnd, D3DCREATE_MIXED_VERTEXPROCESSING | extraFlags, &md3dpp, &mpD3DDevice ); if( FAILED( hr ) ) { hr = pD3D->CreateDevice( adapterToUse, devType, mHWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING | extraFlags, &md3dpp, &mpD3DDevice ); } } // TODO: make this a bit better e.g. go from pure vertex processing to software if( FAILED( hr ) ) { destroy(); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Failed to create Direct3D9 Device: " + Root::getSingleton().getErrorDescription(hr), "D3D9RenderWindow::createD3DResources" ); } } // update device in driver mDriver->setD3DDevice( mpD3DDevice ); // Store references to buffers for convenience mpD3DDevice->GetRenderTarget( 0, &mpRenderSurface ); mpD3DDevice->GetDepthStencilSurface( &mpRenderZBuffer ); // release immediately so we don't hog them mpRenderZBuffer->Release(); } } void D3D9RenderWindow::destroyD3DResources() { if (mIsSwapChain) { SAFE_RELEASE(mpRenderZBuffer); SAFE_RELEASE(mpSwapChain); } else { // ignore depth buffer, access device through driver mpRenderZBuffer = 0; } SAFE_RELEASE(mpRenderSurface); } void D3D9RenderWindow::destroy() { destroyD3DResources(); if (mHWnd && !mIsExternal) { WindowEventUtilities::_removeRenderWindow(this); DestroyWindow(mHWnd); } mHWnd = 0; mActive = false; mClosed = true; } bool D3D9RenderWindow::isVisible() const { return (mHWnd && !IsIconic(mHWnd)); } void D3D9RenderWindow::reposition(int top, int left) { if (mHWnd && !mIsFullScreen) { SetWindowPos(mHWnd, 0, top, left, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } } void D3D9RenderWindow::resize(unsigned int width, unsigned int height) { if (mHWnd && !mIsFullScreen) { RECT rc = { 0, 0, width, height }; AdjustWindowRect(&rc, GetWindowLong(mHWnd, GWL_STYLE), false); width = rc.right - rc.left; height = rc.bottom - rc.top; SetWindowPos(mHWnd, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } } void D3D9RenderWindow::windowMovedOrResized() { if (!mHWnd || IsIconic(mHWnd)) return; RECT rc; // top and left represent outer window position GetWindowRect(mHWnd, &rc); mTop = rc.top; mLeft = rc.left; // width and height represent drawable area only GetClientRect(mHWnd, &rc); unsigned int width = rc.right; unsigned int height = rc.bottom; if (mWidth == width && mHeight == height) return; SAFE_RELEASE( mpRenderSurface ); if (mIsSwapChain) { D3DPRESENT_PARAMETERS pp = md3dpp; pp.BackBufferWidth = width; pp.BackBufferHeight = height; SAFE_RELEASE( mpRenderZBuffer ); SAFE_RELEASE( mpSwapChain ); HRESULT hr = mDriver->getD3DDevice()->CreateAdditionalSwapChain( &pp, &mpSwapChain); if (FAILED(hr)) { StringUtil::StrStreamType str; str << "D3D9RenderWindow: failed to reset device to new dimensions << " << width << " x " << height << ". Trying to recover."; LogManager::getSingleton().logMessage(LML_CRITICAL, str.str()); // try to recover hr = mDriver->getD3DDevice()->CreateAdditionalSwapChain( &md3dpp, &mpSwapChain); if (FAILED(hr)) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Reset window to last size failed", "D3D9RenderWindow::resize" ); } else { md3dpp = pp; mWidth = width; mHeight = height; hr = mpSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mpRenderSurface); hr = mDriver->getD3DDevice()->CreateDepthStencilSurface( mWidth, mHeight, md3dpp.AutoDepthStencilFormat, md3dpp.MultiSampleType, md3dpp.MultiSampleQuality, (md3dpp.Flags & D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL), &mpRenderZBuffer, NULL ); if (FAILED(hr)) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Failed to create depth stencil surface for Swap Chain", "D3D9RenderWindow::resize" ); } } } // primary windows must reset the device else { md3dpp.BackBufferWidth = mWidth = width; md3dpp.BackBufferHeight = mHeight = height; static_cast( Root::getSingleton().getRenderSystem())->_notifyDeviceLost(); } // Notify viewports of resize ViewportList::iterator it = mViewportList.begin(); while( it != mViewportList.end() ) (*it++).second->_updateDimensions(); } void D3D9RenderWindow::swapBuffers( bool waitForVSync ) { // access device through driver LPDIRECT3DDEVICE9 mpD3DDevice = mDriver->getD3DDevice(); if( mpD3DDevice ) { HRESULT hr; if (mIsSwapChain) { hr = mpSwapChain->Present(NULL, NULL, NULL, NULL, 0); } else { hr = mpD3DDevice->Present( NULL, NULL, 0, NULL ); } if( D3DERR_DEVICELOST == hr ) { SAFE_RELEASE(mpRenderSurface); static_cast( Root::getSingleton().getRenderSystem())->_notifyDeviceLost(); } else if( FAILED(hr) ) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Error Presenting surfaces", "D3D9RenderWindow::swapBuffers" ); } } void D3D9RenderWindow::getCustomAttribute( const String& name, void* pData ) { // Valid attributes and their equvalent native functions: // D3DDEVICE : getD3DDevice // WINDOW : getWindowHandle if( name == "D3DDEVICE" ) { LPDIRECT3DDEVICE9 *pDev = (LPDIRECT3DDEVICE9*)pData; *pDev = getD3DDevice(); return; } else if( name == "WINDOW" ) { HWND *pHwnd = (HWND*)pData; *pHwnd = getWindowHandle(); return; } else if( name == "isTexture" ) { bool *b = reinterpret_cast< bool * >( pData ); *b = false; return; } else if( name == "D3DZBUFFER" ) { LPDIRECT3DSURFACE9 *pSurf = (LPDIRECT3DSURFACE9*)pData; *pSurf = mpRenderZBuffer; return; } else if( name == "DDBACKBUFFER" ) { LPDIRECT3DSURFACE9 *pSurf = (LPDIRECT3DSURFACE9*)pData; *pSurf = mpRenderSurface; return; } else if( name == "DDFRONTBUFFER" ) { LPDIRECT3DSURFACE9 *pSurf = (LPDIRECT3DSURFACE9*)pData; *pSurf = mpRenderSurface; return; } } void D3D9RenderWindow::writeContentsToFile(const String& filename) { HRESULT hr; LPDIRECT3DSURFACE9 pSurf=NULL, pTempSurf=NULL; D3DSURFACE_DESC desc; D3DDISPLAYMODE dm; // access device through driver LPDIRECT3DDEVICE9 mpD3DDevice = mDriver->getD3DDevice(); // get display dimensions // this will be the dimensions of the front buffer if (FAILED(hr = mpD3DDevice->GetDisplayMode(0, &dm))) OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Can't get display mode!", "D3D9RenderWindow::writeContentsToFile"); desc.Width = dm.Width; desc.Height = dm.Height; desc.Format = D3DFMT_A8R8G8B8; if (FAILED(hr = mpD3DDevice->CreateOffscreenPlainSurface( desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &pTempSurf, NULL))) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Cannot create offscreen buffer 1!", "D3D9RenderWindow::writeContentsToFile"); } if (FAILED(hr = mpD3DDevice->GetFrontBufferData(0, pTempSurf))) { SAFE_RELEASE(pTempSurf); OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "Can't get front buffer!", "D3D9RenderWindow::writeContentsToFile"); } D3DLOCKED_RECT lockedRect; if(mIsFullScreen) { if (FAILED(hr = pTempSurf->LockRect(&lockedRect, NULL, D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK))) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "can't lock rect!", "D3D9RenderWindow::writeContentsToFile"); } } else { RECT srcRect; GetClientRect(mHWnd, &srcRect); POINT point; point.x = srcRect.left; point.y = srcRect.top; ClientToScreen(mHWnd, &point); srcRect.top = point.y; srcRect.left = point.x; srcRect.bottom += point.y; srcRect.right += point.x; desc.Width = srcRect.right - srcRect.left; desc.Height = srcRect.bottom - srcRect.top; if (FAILED(hr = pTempSurf->LockRect(&lockedRect, &srcRect, D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK))) { OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "can't lock rect!", "D3D9RenderWindow::writeContentsToFile"); } } ImageCodec::ImageData *imgData = new ImageCodec::ImageData(); imgData->width = desc.Width; imgData->height = desc.Height; imgData->format = PF_BYTE_RGB; // Allocate contiguous buffer (surfaces aren't necessarily contiguous) uchar* pBuffer = new uchar[desc.Width * desc.Height * 3]; uint x, y; uchar *pData, *pDest; pData = (uchar*)lockedRect.pBits; pDest = pBuffer; for (y = 0; y < desc.Height; ++y) { uchar *pRow = pData; for (x = 0; x < desc.Width; ++x) { switch(desc.Format) { case D3DFMT_R5G6B5: WORD val; val = *((WORD*)pRow); pRow += 2; *pDest++ = Bitwise::convertBitPattern((WORD)val, (WORD)0xF800, (BYTE)0xFF); *pDest++ = Bitwise::convertBitPattern((WORD)val, (WORD)0x07E0, (BYTE)0xFF); *pDest++ = Bitwise::convertBitPattern((WORD)val, (WORD)0x001F, (BYTE)0xFF); break; case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8: // Actual format is BRGA for some reason *pDest++ = pRow[2]; // R *pDest++ = pRow[1]; // G *pDest++ = pRow[0]; // B pRow += 4; // skip alpha / dummy break; case D3DFMT_R8G8B8: // Actual format is BRGA for some reason *pDest++ = pRow[2]; // R *pDest++ = pRow[1]; // G *pDest++ = pRow[0]; // B pRow += 3; break; } } // increase by one line pData += lockedRect.Pitch; } // Wrap buffer in a chunk MemoryDataStreamPtr stream(new MemoryDataStream(pBuffer, desc.Width * desc.Height * 3, false)); // Get codec size_t pos = filename.find_last_of("."); String extension; if( pos == String::npos ) OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, "Unable to determine image type for '" + filename + "' - invalid extension.", "D3D9RenderWindow::writeContentsToFile" ); while( pos != filename.length() - 1 ) extension += filename[++pos]; // Get the codec Codec * pCodec = Codec::getCodec(extension); // Write out { Codec::CodecDataPtr ptr(imgData); pCodec->codeToFile(stream, filename, ptr); } delete [] pBuffer; SAFE_RELEASE(pTempSurf); SAFE_RELEASE(pSurf); } //----------------------------------------------------------------------------- void D3D9RenderWindow::update(bool swap) { D3D9RenderSystem* rs = static_cast( Root::getSingleton().getRenderSystem()); // access device through driver LPDIRECT3DDEVICE9 mpD3DDevice = mDriver->getD3DDevice(); if (rs->isDeviceLost()) { // Test the cooperative mode first HRESULT hr = mpD3DDevice->TestCooperativeLevel(); if (hr == D3DERR_DEVICELOST) { // device lost, and we can't reset // can't do anything about it here, wait until we get // D3DERR_DEVICENOTRESET; rendering calls will silently fail until // then (except Present, but we ignore device lost there too) SAFE_RELEASE(mpRenderSurface); // need to release if swap chain if (!mIsSwapChain) mpRenderZBuffer = 0; else SAFE_RELEASE (mpRenderZBuffer); Sleep(50); return; } else { // device lost, and we can reset rs->restoreLostDevice(); // Still lost? if (rs->isDeviceLost()) { // Wait a while Sleep(50); return; } if (!mIsSwapChain) { // re-qeuery buffers mpD3DDevice->GetRenderTarget( 0, &mpRenderSurface ); mpD3DDevice->GetDepthStencilSurface( &mpRenderZBuffer ); // release immediately so we don't hog them mpRenderZBuffer->Release(); } else { // Update dimensions incase changed ViewportList::iterator it = mViewportList.begin(); while( it != mViewportList.end() ) (*it++).second->_updateDimensions(); // Actual restoration of surfaces will happen in // D3D9RenderSystem::restoreLostDevice when it calls // createD3DResources for each secondary window } } } RenderWindow::update(swap); } }