diff options
Diffstat (limited to 'canvas/source/directx/dx_5rm.cxx')
-rwxr-xr-x | canvas/source/directx/dx_5rm.cxx | 2283 |
1 files changed, 2283 insertions, 0 deletions
diff --git a/canvas/source/directx/dx_5rm.cxx b/canvas/source/directx/dx_5rm.cxx new file mode 100755 index 000000000000..642ef7bd7db6 --- /dev/null +++ b/canvas/source/directx/dx_5rm.cxx @@ -0,0 +1,2283 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_canvas.hxx" + +#if DIRECTX_VERSION < 0x0900 + +// Nvidia GeForce Go 6800 crashes with a bluescreen if we take the +// maximum texture size, which would be twice as large. this behaviors +// has only been observed on directx5. +// This value is simply the maximum size for textures we request from +// the system, it has absolutely nothing to do with the size of primitives +// we're able to render, both concepts are totally independent from each other. +#define MAX_TEXTURE_SIZE (2048) +#define MIN_TEXTURE_SIZE (32) +//#define FAKE_MAX_NUMBER_TEXTURES (2) +//#define FAKE_MAX_TEXTURE_SIZE (512) + +////////////////////////////////////////////////////////////////////////////////// +// includes +////////////////////////////////////////////////////////////////////////////////// +#include <vcl/syschild.hxx> +#include <vcl/window.hxx> +#include <canvas/debug.hxx> +#include <canvas/verbosetrace.hxx> +#include <canvas/elapsedtime.hxx> +#include <canvas/canvastools.hxx> +#include <canvas/rendering/icolorbuffer.hxx> +#include <canvas/rendering/isurface.hxx> +#include <canvas/rendering/irendermodule.hxx> +#include <tools/diagnose_ex.h> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/vector/b2dsize.hxx> +#include <basegfx/vector/b2isize.hxx> +#include <basegfx/point/b2ipoint.hxx> +#include <basegfx/range/b2irectangle.hxx> +#include <boost/scoped_ptr.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> + +#define COMPILE_MULTIMON_STUBS + +#include "dx_rendermodule.hxx" +#include "dx_surfacegraphics.hxx" +#include <vcl/sysdata.hxx> + +#undef WB_LEFT +#undef WB_RIGHT + +#include "dx_impltools.hxx" +#include <malloc.h> + +#if defined(DX_DEBUG_IMAGES) +# if OSL_DEBUG_LEVEL > 0 +# include <imdebug.h> +# undef min +# undef max +# endif +#endif + +#undef COMPILE_MULTIMON_STUBS + +#include <stdio.h> + +#define MONITOR_DEFAULTTONULL 0x00000000 +#define MONITOR_DEFAULTTOPRIMARY 0x00000001 +#define MONITOR_DEFAULTTONEAREST 0x00000002 + +using namespace ::com::sun::star; + +////////////////////////////////////////////////////////////////////////////////// +// 'dxcanvas' namespace +////////////////////////////////////////////////////////////////////////////////// + +namespace dxcanvas +{ + namespace + { + bool doBlit( const ::basegfx::B2IPoint& rDestPos, + IDirectDrawSurface& rOutSurface, + const ::basegfx::B2IRange& rSourceArea, + IDirectDrawSurface& rSourceSurface, + DDBLTFX* pBltFx, + bool bForceSoftware ) + { + if( !bForceSoftware ) + { + // blit surface to backbuffer + RECT aOutRect = + { + rDestPos.getX(), + rDestPos.getY(), + rDestPos.getX() + static_cast<sal_Int32>(rSourceArea.getWidth()), + rDestPos.getY() + static_cast<sal_Int32>(rSourceArea.getHeight()), + }; + RECT aSourceRect = + { + rSourceArea.getMinX(), + rSourceArea.getMinY(), + rSourceArea.getMaxX(), + rSourceArea.getMaxY() + }; + + if( SUCCEEDED(rOutSurface.Blt( &aOutRect, + &rSourceSurface, + &aSourceRect, + DDBLT_WAIT, + pBltFx )) ) + { + return true; + } + } + + // failed, or forced to use SW copy. attempt manual copy. + bool bResult = false; + + // lock source surface + DDSURFACEDESC aDescSrc; + rtl_fillMemory(&aDescSrc,sizeof(DDSURFACEDESC),0); + aDescSrc.dwSize = sizeof(DDSURFACEDESC); + const DWORD dwSrcFlags = DDLOCK_NOSYSLOCK| + DDLOCK_SURFACEMEMORYPTR| + DDLOCK_WAIT| + DDLOCK_READONLY; + if(SUCCEEDED(rSourceSurface.Lock(NULL, + &aDescSrc, + dwSrcFlags, + NULL))) + { + // lock destination surface + DDSURFACEDESC aDescDst; + rtl_fillMemory(&aDescDst,sizeof(DDSURFACEDESC),0); + aDescDst.dwSize = sizeof(DDSURFACEDESC); + const DWORD dwDstFlags = DDLOCK_NOSYSLOCK| + DDLOCK_SURFACEMEMORYPTR| + DDLOCK_WAIT| + DDLOCK_WRITEONLY; + if(SUCCEEDED(rOutSurface.Lock(NULL, + &aDescDst, + dwDstFlags, + NULL))) + { + sal_uInt32 nSrcFormat; + nSrcFormat = ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwRGBAlphaBitMask)<<12; + nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwRBitMask)<<8; + nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwGBitMask)<<4; + nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwBBitMask); + + sal_uInt32 nDstFormat; + nDstFormat = ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwRGBAlphaBitMask)<<12; + nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwRBitMask)<<8; + nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwGBitMask)<<4; + nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwBBitMask); + + // TODO(E1): Use numeric_cast to catch overflow here + const sal_uInt32 nWidth( static_cast<sal_uInt32>( + rSourceArea.getWidth() ) ); + const sal_uInt32 nHeight( static_cast<sal_uInt32>( + rSourceArea.getHeight() ) ); + + if((nSrcFormat == 0x8888) && (nDstFormat == 0x0565)) + { + // medium range 8888 to 0565 pixel format conversion. + bResult = true; + sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface + + rSourceArea.getMinY()*aDescSrc.lPitch + + (rSourceArea.getMinX()<<2); + sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface + + rDestPos.getY()*aDescDst.lPitch + + (rDestPos.getX()<<1); + for(sal_uInt32 y=0; y<nHeight; ++y) + { + sal_uInt32 *pSrcScanline = (sal_uInt32 *)pSrcSurface; + sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface; + for(sal_uInt32 x=0; x<nWidth; ++x) + { + sal_uInt32 srcPixel = *pSrcScanline++; + sal_uInt16 dstPixel; + dstPixel = (sal_uInt16)((srcPixel & 0x0000F8) >> 3); + dstPixel |= (srcPixel & 0x00FC00) >> 5; + dstPixel |= (srcPixel & 0xF80000) >> 8; + *pDstScanline++ = dstPixel; + } + pSrcSurface += aDescSrc.lPitch; + pDstSurface += aDescDst.lPitch; + } + } + else if((nSrcFormat == 0x8888) && (nDstFormat == 0x0888)) + { + // medium range 8888 to 0888 pixel format conversion. + bResult = true; + sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface + + rSourceArea.getMinY()*aDescSrc.lPitch + + (rSourceArea.getMinX()<<2); + sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface + + rDestPos.getY()*aDescDst.lPitch + + (rDestPos.getX()<<2); + for(sal_uInt32 y=0; y<nHeight; ++y) + { + sal_uInt32 *pSrcScanline = (sal_uInt32 *)pSrcSurface; + sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface; + for(sal_uInt32 x=0; x<nWidth; ++x) + { + *pDstScanline++ = (sal_uInt16)*pSrcScanline++; + } + pSrcSurface += aDescSrc.lPitch; + pDstSurface += aDescDst.lPitch; + } + } + else if((nSrcFormat == 0x8888) && (nDstFormat == 0x1555)) + { + // medium range 8888 to 1555 pixel format conversion. + bResult = true; + sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface + + rSourceArea.getMinY()*aDescSrc.lPitch + + (rSourceArea.getMinX()<<2); + sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface + + rDestPos.getY()*aDescDst.lPitch + + (rDestPos.getX()<<1); + for(sal_uInt32 y=0; y<nHeight; ++y) + { + sal_uInt32 *pSrcScanline = (sal_uInt32*)pSrcSurface; + sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface; + for(sal_uInt32 x=0; x<nWidth; ++x) + { + sal_uInt32 srcPixel = *pSrcScanline++; + sal_uInt16 dstPixel; + dstPixel = (sal_uInt16)((srcPixel & 0x000000F8) >> 3); + dstPixel |= (srcPixel & 0x0000F800) >> 6; + dstPixel |= (srcPixel & 0x00F80000) >> 9; + dstPixel |= (srcPixel & 0x80000000) >> 16; + *pDstScanline++ = dstPixel; + } + pSrcSurface += aDescSrc.lPitch; + pDstSurface += aDescDst.lPitch; + } + } + + // unlock destination surface + rOutSurface.Unlock(NULL); + } + + // unlock source surface + rSourceSurface.Unlock(NULL); + } + + return bResult; + } + + void dumpSurface( const COMReference<IDirectDrawSurface> &pSurface, const char *szFilename ) + { + if(!(pSurface.get())) + return; + + DDSURFACEDESC aSurfaceDesc; + rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 ); + aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); + + if( FAILED(pSurface->Lock( NULL, + &aSurfaceDesc, + DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY, + NULL)) ) + return; + + const std::size_t dwBitmapSize(aSurfaceDesc.dwWidth*aSurfaceDesc.dwHeight*4); + sal_uInt8 *pBuffer = static_cast<sal_uInt8 *>(_alloca(dwBitmapSize)); + if(pBuffer) + { + sal_uInt8 *pSource = reinterpret_cast<sal_uInt8 *>(aSurfaceDesc.lpSurface); + sal_uInt8 *pDest = reinterpret_cast<sal_uInt8 *>(pBuffer); + const std::size_t dwDestPitch(aSurfaceDesc.dwWidth<<2); + pDest += aSurfaceDesc.dwHeight*dwDestPitch; + for(sal_uInt32 y=0; y<aSurfaceDesc.dwHeight; ++y) + { + pDest -= dwDestPitch; + rtl_copyMemory( pDest, pSource, dwDestPitch ); + pSource += aSurfaceDesc.lPitch; + } + + if(FILE *fp = fopen(szFilename,"wb")) + { + BITMAPINFOHEADER bitmapInfo; + + bitmapInfo.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.biWidth = aSurfaceDesc.dwWidth; + bitmapInfo.biHeight = aSurfaceDesc.dwHeight; + bitmapInfo.biPlanes = 1; + bitmapInfo.biBitCount = 32; + bitmapInfo.biCompression = BI_RGB; + bitmapInfo.biSizeImage = 0; + bitmapInfo.biXPelsPerMeter = 0; + bitmapInfo.biYPelsPerMeter = 0; + bitmapInfo.biClrUsed = 0; + bitmapInfo.biClrImportant = 0; + + const std::size_t dwFileSize(sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwBitmapSize); + + BITMAPFILEHEADER header; + header.bfType = 'MB'; + header.bfSize = dwFileSize; + header.bfReserved1 = 0; + header.bfReserved2 = 0; + header.bfOffBits = sizeof(BITMAPFILEHEADER) + bitmapInfo.biSize; + + fwrite(&header,1,sizeof(BITMAPFILEHEADER),fp); + fwrite(&bitmapInfo,1,sizeof(BITMAPINFOHEADER),fp); + fwrite(pBuffer,1,dwBitmapSize,fp); + + fclose(fp); + } + } + + pSurface->Unlock(NULL); + } + + void clearSurface( const COMReference<IDirectDrawSurface>& pSurface ) + { + if(!(pSurface.is())) + return; + + DDBLTFX aBltFx; + + rtl_fillMemory( &aBltFx, + sizeof(DDBLTFX), 0 ); + aBltFx.dwSize = sizeof(DDBLTFX); + aBltFx.dwFillColor = 0; + + pSurface->Blt( NULL, + NULL, + NULL, + DDBLT_COLORFILL | DDBLT_WAIT, + &aBltFx ); + } + + // Define struct for MonitorEntry + struct MonitorEntry + { + GUID mnGUID; + HMONITOR mhMonitor; + MONITORINFO maMonitorInfo; + }; + + // define type for MonitorList + typedef ::std::vector< MonitorEntry > MonitorList; + + // Win32 system callback for DirectDrawEnumerateExA call + BOOL WINAPI EnumerateExA_Callback( GUID FAR* lpGUID, + LPSTR /*lpDriverDescription*/, + LPSTR /*lpDriverName*/, + LPVOID lpContext, + HMONITOR hMonitor ) + { + if(lpGUID) + { + MonitorList* pMonitorList = (MonitorList*)lpContext; + MonitorEntry aEntry; + + aEntry.mnGUID = *lpGUID; + aEntry.mhMonitor = hMonitor; + aEntry.maMonitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo( hMonitor, + &aEntry.maMonitorInfo ); + + pMonitorList->push_back(aEntry); + } + + return DDENUMRET_OK; + } + + void fillMonitorList( MonitorList& rMonitorList ) + { + // Try to fill MonitorList. If neither lib or call to + // DirectDrawEnumerateExA does not exist, it's an old + // DX version (< 5.0), or system does not support + // multiple monitors. + HINSTANCE hInstance = LoadLibrary("ddraw.dll"); + + if(hInstance) + { + LPDIRECTDRAWENUMERATEEX lpDDEnumEx = + (LPDIRECTDRAWENUMERATEEX)GetProcAddress(hInstance,"DirectDrawEnumerateExA"); + + if(lpDDEnumEx) + lpDDEnumEx( (LPDDENUMCALLBACKEXA) EnumerateExA_Callback, + &rMonitorList, + DDENUM_ATTACHEDSECONDARYDEVICES ); + + FreeLibrary(hInstance); + } + } + + IDirectDraw2* createDirectDraw( const MonitorList& rMonitorList, + MONITORINFO& rMonitorInfo, + HWND renderWindow ) + { + GUID* gpSelectedDriverGUID = NULL; + + // if we have multiple monitors, choose a gpSelectedDriverGUID from monitor list + HMONITOR hMonitor = MonitorFromWindow(renderWindow, + MONITOR_DEFAULTTONEAREST); + + MonitorList::const_iterator aCurr = rMonitorList.begin(); + const MonitorList::const_iterator aEnd = rMonitorList.end(); + while( !gpSelectedDriverGUID && aCurr != aEnd ) + { + if(hMonitor == aCurr->mhMonitor) + { + // This is the monitor we are running on + gpSelectedDriverGUID = const_cast<GUID*>(&aCurr->mnGUID); + rMonitorInfo = aCurr->maMonitorInfo; + } + + ++aCurr; + } + + IDirectDraw* pDirectDraw; + if( FAILED( DirectDrawCreate( gpSelectedDriverGUID, &pDirectDraw, NULL ))) + return NULL; + + IDirectDraw2* pDirectDraw2; + if( FAILED( pDirectDraw->QueryInterface( IID_IDirectDraw2, (LPVOID*)&pDirectDraw2 ))) + return NULL; + + // queryInterface bumped up the refcount, so release the + // reference to the original IDirectDraw interface. + pDirectDraw->Release(); + + return pDirectDraw2; + } + + HRESULT WINAPI EnumTextureFormatsCallback( LPDDSURFACEDESC pSurfaceDesc, + LPVOID pContext ) + { + // dirty cast of given context back to result ModeSelectContext + DDPIXELFORMAT* pResult = (DDPIXELFORMAT*)pContext; + + if( pResult == NULL || pSurfaceDesc == NULL ) + return DDENUMRET_CANCEL; + + VERBOSE_TRACE( "EnumTextureFormatsCallback: advertised texture format has dwRGBBitCount %d, dwRBitMask %x, " + "dwGBitMask %x, dwBBitMask %x and dwRGBAlphaBitMask %x. The format uses %s alpha.", + pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount, + pSurfaceDesc->ddpfPixelFormat.dwRBitMask, + pSurfaceDesc->ddpfPixelFormat.dwGBitMask, + pSurfaceDesc->ddpfPixelFormat.dwBBitMask, + pSurfaceDesc->ddpfPixelFormat.dwRGBAlphaBitMask, + pSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPREMULT ? "premultiplied" : "non-premultiplied" ); + + // Only accept RGB surfaces with alpha channel + if( (DDPF_ALPHAPIXELS | DDPF_RGB) == + (pSurfaceDesc->ddpfPixelFormat.dwFlags & (DDPF_ALPHAPIXELS | DDPF_RGB)) ) + { + // ignore formats with the DDPF_ALPHAPREMULT flag + if(!(pSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPREMULT)) + { + // take widest alpha channel available + if( pSurfaceDesc->ddpfPixelFormat.dwAlphaBitDepth > pResult->dwAlphaBitDepth ) + { + // take new format + rtl_copyMemory( pResult, &pSurfaceDesc->ddpfPixelFormat, sizeof(DDPIXELFORMAT) ); + } + else if( pSurfaceDesc->ddpfPixelFormat.dwAlphaBitDepth == pResult->dwAlphaBitDepth ) + { + // tie-breaking: take highest bitcount + if( pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount > pResult->dwRGBBitCount ) + { + // take new format + rtl_copyMemory( pResult, &pSurfaceDesc->ddpfPixelFormat, sizeof(DDPIXELFORMAT) ); + } + } + } + } + + return DDENUMRET_OK; + } + + class DXRenderModule; + + ////////////////////////////////////////////////////////////////////////////////// + // DXSurface + ////////////////////////////////////////////////////////////////////////////////// + + /** ISurface implemenation. + + @attention holds the DXRenderModule via non-refcounted + reference! This is safe with current state of affairs, since + the canvas::PageManager holds surface and render module via + shared_ptr (and makes sure all surfaces are deleted before its + render module member goes out of scope). + */ + class DXSurface : public canvas::ISurface + { + public: + DXSurface( DXRenderModule& rRenderModule, + const ::basegfx::B2ISize& rSize ); + ~DXSurface(); + + virtual bool selectTexture(); + virtual bool isValid(); + virtual bool update( const ::basegfx::B2IPoint& rDestPos, + const ::basegfx::B2IRange& rSourceRect, + ::canvas::IColorBuffer& rSource ); + virtual ::basegfx::B2IVector getSize(); + + private: + /// Guard local methods against concurrent acces to RenderModule + class ImplRenderModuleGuard : private ::boost::noncopyable + { + public: + explicit inline ImplRenderModuleGuard( DXRenderModule& rRenderModule ); + inline ~ImplRenderModuleGuard(); + + private: + DXRenderModule& mrRenderModule; + }; + + DXRenderModule& mrRenderModule; + + COMReference<IDirectDrawSurface> mpSurface; + COMReference<IDirect3DTexture2> mpTexture; + + ::basegfx::B2IVector maSize; + }; + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule + ////////////////////////////////////////////////////////////////////////////////// + + /// Default implementation of IDXRenderModule + class DXRenderModule : public IDXRenderModule + { + public: + explicit DXRenderModule( const ::Window& rWindow ); + + virtual void lock() const { maMutex.acquire(); } + virtual void unlock() const { maMutex.release(); } + + virtual COMReference<IDirectDrawSurface> + createSystemMemorySurface( const ::basegfx::B2IVector& rSize ); + + virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea, + const ::basegfx::B2IRectangle& rCurrWindowArea ); + + virtual void resize( const ::basegfx::B2IRange& rect ); + virtual HWND getHWND() const { return mhWnd; } + virtual void disposing(); + virtual void screenShot(); + virtual ::basegfx::B2IVector getPageSize(); + virtual ::canvas::ISurfaceSharedPtr createSurface( const ::basegfx::B2IVector& surfaceSize ); + virtual void beginPrimitive( PrimitiveType eType ); + virtual void endPrimitive(); + virtual void pushVertex( const ::canvas::Vertex& vertex ); + virtual bool isError(); + + const D3DDEVICEDESC& getDeviceDesc() const { return maDeviceDesc; } + const DDPIXELFORMAT& getTextureFormat() const { return maTextureFormat; } + COMReference<IDirectDraw2> getDirectDraw() { return mpDirectDraw; } + COMReference< IDirect3DDevice2 > getDevice() { return mpDirect3DDevice; } + + void flushVertexCache(); + + struct ModeSelectContext + { + DDSURFACEDESC selectedDesc; + ::basegfx::B2ISize requestedSize; + }; + + /** Query actual size of the device + + This is especially interesting for fullscreen devices + */ + ::basegfx::B2ISize getFramebufferSize() const; + + /** Query the amount of memory available for new surfaces + + This might differ from getAvailableTextureMem() + @see getAvailableTextureMem() + + @param bWithAGPMema + When true, returned value includes non-local, + i.e. AGP-able memory, too. + + @return the amount of free surface mem + */ + std::size_t getAvailableSurfaceMem( bool bWithAGPMem=true ) const; + + /** Query the amount of memory available for new textures + + This might differ from getAvailableSurfaceMem() + @see getAvailableSurfaceMem() + + @param bWithAGPMema + When true, returned value includes non-local, + i.e. AGP-able memory, too. + + @return the amount of free texture mem + */ + std::size_t getAvailableTextureMem( bool bWithAGPMem=true ) const; + + private: + bool queryCaps(); + bool validateCaps(); + bool setup3DDevice(); + unsigned int getDisplayFormat() const; + + void convert2Screen( ::basegfx::B2IPoint& io_rDestPos, + ::basegfx::B2IRange& io_rDestArea ); + + void renderInfoText( const ::rtl::OUString& rStr, + const Gdiplus::PointF& rPos ) const; + void renderFPSCounter() const; + void renderMemAvailable() const; + + bool create( const ::Window& rWindow ); + bool validateMainSurfaces(); + + /** This object represents the DirectX state machine. In order + to serialize access to DirectX's global state, a global + mutex is required. + */ + static ::osl::Mutex maMutex; + + HWND mhWnd; + ::boost::scoped_ptr<SystemChildWindow> mpWindow; + ::basegfx::B2IVector maSize; + + ModeSelectContext maSelectedFullscreenMode; + DDPIXELFORMAT maTextureFormat; + + MONITORINFO maMonitorInfo; // monitor info for mpDirectDraw's monitor + COMReference<IDirectDraw2> mpDirectDraw; + COMReference<IDirectDrawSurface> mpPrimarySurface; + COMReference<IDirectDrawSurface> mpBackBufferSurface; + + COMReference< IDirect3D2 > mpDirect3D; + COMReference< IDirect3DDevice2 > mpDirect3DDevice; + + mutable ::canvas::tools::ElapsedTime maLastUpdate; // for the frame counter + + D3DDEVICEDESC maDeviceDesc; + + typedef std::vector<canvas::Vertex> vertexCache_t; + vertexCache_t maVertexCache; + std::size_t mnCount; + + int mnBeginSceneCount; + + const bool mbPageFlipping; + bool mbHasNoTearingBlt; + bool mbError; + PrimitiveType meType; + + ::canvas::ISurfaceSharedPtr mpTexture; + ::basegfx::B2IVector maPageSize; + }; + + ::osl::Mutex DXRenderModule::maMutex; + + ////////////////////////////////////////////////////////////////////////////////// + // DXSurface::ImplRenderModuleGuard + ////////////////////////////////////////////////////////////////////////////////// + + inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard( + DXRenderModule& rRenderModule ) : + mrRenderModule( rRenderModule ) + { + mrRenderModule.lock(); + } + + inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard() + { + mrRenderModule.unlock(); + } + +#ifdef FAKE_MAX_NUMBER_TEXTURES + static sal_uInt32 gNumSurfaces = 0; +#endif + + void fillRect( sal_uInt32 *pDest, + sal_uInt32 dwWidth, + sal_uInt32 dwHeight, + sal_uInt32 dwPitch, + sal_uInt32 dwColor ) + { + for(sal_uInt32 i=0; i<dwWidth; ++i) + { + pDest[i]=dwColor; + pDest[((dwHeight-1)*dwPitch)+i]=dwColor; + } + + for(sal_uInt32 j=0; j<dwHeight; ++j) + { + pDest[0]=dwColor; + pDest[dwWidth-1]=dwColor; + pDest += dwPitch; + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXSurface::DXSurface + ////////////////////////////////////////////////////////////////////////////////// + + DXSurface::DXSurface( DXRenderModule& rRenderModule, + const ::basegfx::B2ISize& rSize ) : + mrRenderModule(rRenderModule), + mpTexture(NULL), + mpSurface(NULL), + maSize() + { + ImplRenderModuleGuard aGuard( mrRenderModule ); + +#ifdef FAKE_MAX_NUMBER_TEXTURES + ++gNumSurfaces; + if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES) + return; +#endif + +#ifdef FAKE_MAX_TEXTURE_SIZE + if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE) + return; + if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE) + return; +#endif + + ENSURE_ARG_OR_THROW(rSize.getX() > 0 && rSize.getY() > 0, + "DXSurface::DXSurface(): request for zero-sized surface"); + + const D3DDEVICEDESC &deviceDesc = rRenderModule.getDeviceDesc(); + + DDSURFACEDESC aSurfaceDesc; + rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 ); + aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); + aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + aSurfaceDesc.dwWidth = ::std::min(deviceDesc.dwMaxTextureWidth,::canvas::tools::nextPow2(rSize.getX())); + aSurfaceDesc.dwHeight = ::std::min(deviceDesc.dwMaxTextureHeight,::canvas::tools::nextPow2(rSize.getY())); + aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE | + DDSCAPS_VIDEOMEMORY | + DDSCAPS_LOCALVIDMEM; + rtl_copyMemory(&aSurfaceDesc.ddpfPixelFormat,&rRenderModule.getTextureFormat(),sizeof(DDPIXELFORMAT)); + + IDirectDrawSurface *pSurface; + COMReference<IDirectDraw2> pDirectDraw(rRenderModule.getDirectDraw()); + HRESULT hr = pDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); + if(FAILED(hr)) + { + // if the call failed due to 'out of videomemory', + // retry with request for AGP memory. + if(DDERR_OUTOFVIDEOMEMORY == hr) + { + aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE | + DDSCAPS_VIDEOMEMORY | + DDSCAPS_NONLOCALVIDMEM; + hr = pDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); + } + } + + if(SUCCEEDED(hr)) + { + IDirect3DTexture2* pTexture; + if( FAILED(pSurface->QueryInterface(IID_IDirect3DTexture2, (LPVOID *)&pTexture)) ) + { + pSurface->Release(); + return; + } + + maSize.setX(aSurfaceDesc.dwWidth); + maSize.setY(aSurfaceDesc.dwHeight); + + mpSurface=COMReference<IDirectDrawSurface>(pSurface); + mpTexture=COMReference<IDirect3DTexture2>(pTexture); + + // #122683# Clear texture, to avoid ugly artifacts at the + // border to invisible sprite areas (note that the textures + // are usually only partly utilized). + clearSurface( mpSurface ); + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXSurface::~DXSurface + ////////////////////////////////////////////////////////////////////////////////// + + DXSurface::~DXSurface() + { + ImplRenderModuleGuard aGuard( mrRenderModule ); + +#ifdef FAKE_MAX_NUMBER_TEXTURES + gNumSurfaces--; +#endif + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXSurface::selectTexture + ////////////////////////////////////////////////////////////////////////////////// + + bool DXSurface::selectTexture() + { + ImplRenderModuleGuard aGuard( mrRenderModule ); + + mrRenderModule.flushVertexCache(); + + D3DTEXTUREHANDLE aTextureHandle; + if(FAILED(mpTexture->GetHandle( + mrRenderModule.getDevice().get(), + &aTextureHandle))) + { + return false; + } + + // select texture for next primitive + if(FAILED(mrRenderModule.getDevice()->SetRenderState( + D3DRENDERSTATE_TEXTUREHANDLE,aTextureHandle))) + { + return false; + } + +#if defined(DX_DEBUG_IMAGES) +# if OSL_DEBUG_LEVEL > 0 + if( mpSurface.is() ) + { + DDSURFACEDESC aSurfaceDesc; + rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 ); + aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); + + if( SUCCEEDED(mpSurface->Lock( NULL, + &aSurfaceDesc, + DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY, + NULL)) ) + { + imdebug( "rgba w=%d h=%d %p", + aSurfaceDesc.dwWidth, + aSurfaceDesc.dwHeight, + aSurfaceDesc.lpSurface ); + + mpSurface->Unlock(NULL); + } + } +# endif +#endif + + return true; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXSurface::isValid + ////////////////////////////////////////////////////////////////////////////////// + + bool DXSurface::isValid() + { + ImplRenderModuleGuard aGuard( mrRenderModule ); + + if(!(mpSurface.is())) + return false; + + if(mpSurface->IsLost() == DDERR_SURFACELOST) + { + mpSurface->Restore(); + return false; + } + + return true; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXSurface::update + ////////////////////////////////////////////////////////////////////////////////// + + bool DXSurface::update( const ::basegfx::B2IPoint& rDestPos, + const ::basegfx::B2IRange& rSourceRect, + ::canvas::IColorBuffer& rSource ) + { + ImplRenderModuleGuard aGuard( mrRenderModule ); + + // can't update if surface is not valid, that means + // either not existent nor restored... + if(!(isValid())) + return false; + + DDSURFACEDESC aSurfaceDesc; + rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 ); + aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); + + // TODO(P2): only lock the region we want to update + if( FAILED(mpSurface->Lock( NULL, + &aSurfaceDesc, + DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_WRITEONLY, + NULL)) ) + return false; + + if(sal_uInt8* pImage = rSource.lock()) + { + switch( rSource.getFormat() ) + { + case ::canvas::IColorBuffer::FMT_A8R8G8B8: + { + const std::size_t nSourceBytesPerPixel(4); + const std::size_t nSourcePitchInBytes(rSource.getStride()); + pImage += rSourceRect.getMinY()*nSourcePitchInBytes; + pImage += rSourceRect.getMinX()*nSourceBytesPerPixel; + + // calculate the destination memory address + sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+ + (rDestPos.getY()*aSurfaceDesc.lPitch) + + (4*rDestPos.getX())); + + const sal_uInt32 nNumBytesToCopy( + static_cast<sal_uInt32>( + rSourceRect.getWidth())* + nSourceBytesPerPixel); + const sal_uInt64 nNumLines(rSourceRect.getHeight()); + + for(sal_uInt32 i=0; i<nNumLines; ++i) + { + rtl_copyMemory(pDst,pImage,nNumBytesToCopy); + + pDst += aSurfaceDesc.lPitch; + pImage += nSourcePitchInBytes; + } + } + break; + + case ::canvas::IColorBuffer::FMT_R8G8B8: + { + const std::size_t nSourceBytesPerPixel(3); + const std::size_t nSourcePitchInBytes(rSource.getStride()); + pImage += rSourceRect.getMinY()*nSourcePitchInBytes; + pImage += rSourceRect.getMinX()*nSourceBytesPerPixel; + + // calculate the destination memory address + sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+ + (rDestPos.getY()*aSurfaceDesc.lPitch) + + (4*rDestPos.getX())); + + const sal_uInt64 nNumColumns(rSourceRect.getWidth()); + const sal_uInt64 nNumLines(rSourceRect.getHeight()); + for(sal_uInt32 i=0; i<nNumLines; ++i) + { + sal_uInt32 *pDstScanline = reinterpret_cast<sal_uInt32 *>(pDst); + sal_uInt8 *pSrcScanline = reinterpret_cast<sal_uInt8 *>(pImage); + for(sal_uInt32 x=0; x<nNumColumns; ++x) + { + sal_uInt32 color(0xFF000000); + color |= pSrcScanline[2]<<16; + color |= pSrcScanline[1]<<8; + color |= pSrcScanline[0]; + pSrcScanline += 3; + *pDstScanline++ = color; + } + + pDst += aSurfaceDesc.lPitch; + pImage += nSourcePitchInBytes; + } + } + break; + + case ::canvas::IColorBuffer::FMT_X8R8G8B8: + { + const std::size_t nSourceBytesPerPixel(4); + const std::size_t nSourcePitchInBytes(rSource.getStride()); + pImage += rSourceRect.getMinY()*nSourcePitchInBytes; + pImage += rSourceRect.getMinX()*nSourceBytesPerPixel; + + // calculate the destination memory address + sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+ + (rDestPos.getY()*aSurfaceDesc.lPitch) + + (4*rDestPos.getX())); + + const sal_uInt64 nNumLines(rSourceRect.getHeight()); + + for(sal_uInt32 i=0; i<nNumLines; ++i) + { + sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage); + sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst); + for(sal_uInt32 j=0; j<rSourceRect.getWidth(); ++j) + pDst32[j] = 0xFF000000 | pSrc32[j]; + + pDst += aSurfaceDesc.lPitch; + pImage += nSourcePitchInBytes; + } + } + break; + + default: + ENSURE_OR_RETURN_FALSE(false, + "DXSurface::update(): Unknown/unimplemented buffer format" ); + break; + } + + rSource.unlock(); + } + + return SUCCEEDED(mpSurface->Unlock(NULL)); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXSurface::getSize + ////////////////////////////////////////////////////////////////////////////////// + + ::basegfx::B2IVector DXSurface::getSize() + { + return maSize; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::DXRenderModule + ////////////////////////////////////////////////////////////////////////////////// + + DXRenderModule::DXRenderModule( const ::Window& rWindow ) : + mhWnd(0), + mpWindow(), + maSize(), + maSelectedFullscreenMode(), + maTextureFormat(), + maMonitorInfo(), + mpDirectDraw(), + mpPrimarySurface(), + mpBackBufferSurface(), + mpDirect3D(), + mpDirect3DDevice(), + maLastUpdate(), + maDeviceDesc(), + maVertexCache(), + mnCount(0), + mnBeginSceneCount(0), + mbPageFlipping( false ), + mbHasNoTearingBlt( false ), + mbError( false ), + meType( PRIMITIVE_TYPE_UNKNOWN ), + mpTexture(), + maPageSize() + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + if(!(create(rWindow))) + { + throw lang::NoSupportException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "Could not create DirectX device!") ),NULL); + } + + // allocate a single texture surface which can be used later. + // we also use this to calibrate the page size. + ::basegfx::B2IVector aPageSize( + ::std::min( + static_cast<sal_uInt32>(maDeviceDesc.dwMaxTextureWidth), + static_cast<sal_uInt32>(MAX_TEXTURE_SIZE)), + ::std::min( + static_cast<sal_uInt32>(maDeviceDesc.dwMaxTextureHeight), + static_cast<sal_uInt32>(MAX_TEXTURE_SIZE))); + while(true) + { + mpTexture = ::canvas::ISurfaceSharedPtr( + new DXSurface(*this,aPageSize)); + if(mpTexture->isValid()) + break; + + aPageSize.setX(aPageSize.getX()>>1); + aPageSize.setY(aPageSize.getY()>>1); + if((aPageSize.getX() < MIN_TEXTURE_SIZE) || + (aPageSize.getY() < MIN_TEXTURE_SIZE)) + { + throw lang::NoSupportException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "Could not create DirectX device!") ),NULL); + } + } + maPageSize=aPageSize; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::create + ////////////////////////////////////////////////////////////////////////////////// + + bool DXRenderModule::create( const ::Window& rWindow ) + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + maVertexCache.reserve(1024); + + mpWindow.reset( + new SystemChildWindow( + const_cast<Window *>(&rWindow), 0) ); + + // system child window must not receive mouse events + mpWindow->SetMouseTransparent( TRUE ); + + // parent should receive paint messages as well + // [PARENTCLIPMODE_NOCLIP], the argument is here + // passed as plain numeric value since the stupid + // define utilizes a USHORT cast. + mpWindow->SetParentClipMode(0x0002); + + // the system child window must not clear its background + mpWindow->EnableEraseBackground( FALSE ); + + mpWindow->SetControlForeground(); + mpWindow->SetControlBackground(); + mpWindow->EnablePaint(FALSE); + + const SystemEnvData *pData = mpWindow->GetSystemData(); + const HWND hwnd(reinterpret_cast<HWND>(pData->hWnd)); + mhWnd = const_cast<HWND>(hwnd); + + ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND>(mhWnd) ), + "DXRenderModuleDXRenderModuleWin32() No valid HWND given." ); + + // retrieve position and size of the parent window + const ::Size &rSizePixel(rWindow.GetSizePixel()); + + // remember the size of the parent window, since we + // need to use this for our child window. + maSize.setX(static_cast<sal_Int32>(rSizePixel.Width())); + maSize.setY(static_cast<sal_Int32>(rSizePixel.Height())); + + // let the child window cover the same size as the parent window. + mpWindow->SetPosSizePixel(0,0,maSize.getX(),maSize.getY()); + + MonitorList aMonitorList; + fillMonitorList( aMonitorList ); + + mpDirectDraw = COMReference<IDirectDraw2>( + createDirectDraw(aMonitorList, maMonitorInfo, mhWnd)); + + if(!mpDirectDraw.is()) + return false; + + if( !queryCaps() ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): GetCaps failed" ); + mpDirectDraw.reset(); + return false; + } + + if( !validateCaps() ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): Insufficient DirectX capabilities, failed" ); + mpDirectDraw.reset(); + return false; + } + + if( FAILED( mpDirectDraw->SetCooperativeLevel( mhWnd, + DDSCL_NORMAL|DDSCL_MULTITHREADED|DDSCL_FPUPRESERVE ) ) ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): SetCooperativeLevel failed" ); + mpDirectDraw.reset(); + return false; + } + + // setup query struct + rtl_fillMemory( &maSelectedFullscreenMode.selectedDesc, + sizeof(DDSURFACEDESC), 0 ); + maSelectedFullscreenMode.selectedDesc.dwSize = sizeof(DDSURFACEDESC); + + // read current display mode, e.g. for screen dimension + if( FAILED( mpDirectDraw->GetDisplayMode( &maSelectedFullscreenMode.selectedDesc )) ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): GetDisplayMode failed" ); + mpDirectDraw.reset(); + return false; + } + + // check for supported primary surface formats... + unsigned int nDisplayFormat = getDisplayFormat() & 0x00000FFF; + if(nDisplayFormat != 0x888 && nDisplayFormat != 0x565) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): Unsupported DisplayFormat" ); + mpDirectDraw.reset(); + return false; + } + + // create primary surface reference + DDSURFACEDESC aSurfaceDesc; + IDirectDrawSurface* pPrimarySurface; + + rtl_fillMemory( &aSurfaceDesc, + sizeof(DDSURFACEDESC), 0 ); + aSurfaceDesc.dwSize = sizeof(aSurfaceDesc); + aSurfaceDesc.dwFlags = DDSD_CAPS; + aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE; + + if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pPrimarySurface, NULL)) ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): CreateSurface failed" ); + mpDirectDraw.reset(); + return false; + } + + mpPrimarySurface = COMReference< IDirectDrawSurface >(pPrimarySurface); + + // create a Clipper and associate it with the primary surface + // and the render window + LPDIRECTDRAWCLIPPER pClipper; + if( FAILED(mpDirectDraw->CreateClipper( 0, &pClipper, NULL )) ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): CreateClipper failed" ); + mpPrimarySurface.reset(); + mpDirectDraw.reset(); + return false; + } + if( FAILED(pClipper->SetHWnd(0, mhWnd)) ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): Clipper->SetHWnd failed" ); + pClipper->Release(); + mpPrimarySurface.reset(); + mpDirectDraw.reset(); + return false; + } + if( FAILED(mpPrimarySurface->SetClipper( pClipper )) ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): SetClipper failed" ); + pClipper->Release(); + mpPrimarySurface.reset(); + mpDirectDraw.reset(); + return false; + } + + // clipper is now owned by mpPrimarySurface, release our reference + pClipper->Release(); + + // TODO(F3): Check whether palette needs any setup here + + // get us a backbuffer for simulated flipping + IDirectDrawSurface* pSurface; + + // Strictly speaking, we don't need a full screen worth of + // backbuffer here. We could also scale dynamically with + // the current window size, but this will make it + // necessary to temporarily have two buffers while copying + // from the old to the new one. What's more, at the time + // we need a larger buffer, DX might not have sufficient + // resources available, and we're then left with too small + // a back buffer, and no way of falling back to a + // different canvas implementation. + const ::basegfx::B2ISize aSize( getFramebufferSize() ); + + rtl_fillMemory( &aSurfaceDesc, + sizeof(DDSURFACEDESC), 0 ); + aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); + aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + aSurfaceDesc.dwHeight= aSize.getY(); + aSurfaceDesc.dwWidth = aSize.getX(); + + aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; + + HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); + + if( FAILED( nRes ) ) + { + if( nRes == DDERR_OUTOFVIDEOMEMORY ) + { + // local vid mem failed. Maybe AGP mem works? + aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; + if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL)) ) + { + // no chance, go defunct, and exit + VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" ); + mpPrimarySurface.reset(); + mpDirectDraw.reset(); + return false; + } + + VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer reverted to non-local video mem" ); + } + else + { + // no chance, go defunct, and exit + VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" ); + mpPrimarySurface.reset(); + mpDirectDraw.reset(); + return false; + } + } + + VERBOSE_TRACE( "Device::Device(): created backbuffer of size %d times %d pixel", + aSurfaceDesc.dwWidth, + aSurfaceDesc.dwHeight ); + + mpBackBufferSurface = COMReference< IDirectDrawSurface >(pSurface); + clearSurface(mpBackBufferSurface); + + if( !setup3DDevice() ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::Device(): setup3DDevice failed" ); + mpBackBufferSurface.reset(); + mpPrimarySurface.reset(); + mpDirectDraw.reset(); + return false; + } + + mpWindow->Show(); + + return true; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::getSize + ////////////////////////////////////////////////////////////////////////////////// + + ::basegfx::B2ISize DXRenderModule::getFramebufferSize() const + { + return mpDirectDraw.is() ? + ::basegfx::B2ISize( maSelectedFullscreenMode.selectedDesc.dwWidth, + maSelectedFullscreenMode.selectedDesc.dwHeight ) : + ::basegfx::B2ISize(); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::setup3DDevice + ////////////////////////////////////////////////////////////////////////////////// + + bool DXRenderModule::setup3DDevice() + { + // create and setup 3D device + // ========================== + LPDIRECT3D2 pDirect3D; + if( FAILED( mpDirectDraw->QueryInterface( IID_IDirect3D2, (LPVOID*)&pDirect3D ) ) ) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::setup3DDevice(): QueryInterface() for Direct3D failed" ); + return false; + } + + mpDirect3D = COMReference< IDirect3D2 >(pDirect3D); + + LPDIRECT3DDEVICE2 pDirect3DDevice; + // try HW-accelerated device first + if( FAILED(mpDirect3D->CreateDevice( IID_IDirect3DHALDevice, + mpBackBufferSurface.get(), + &pDirect3DDevice )) ) + { + // no HW 3D support - go defunct, and exit + VERBOSE_TRACE( "Device::setup3DDevice(): CreateDevice() for HW Direct3D rendering failed" ); + mpDirect3D.reset(); + return false; + } + + D3DDEVICEDESC aHELDeviceDesc; + rtl_fillMemory(&maDeviceDesc,sizeof(maDeviceDesc),0); + rtl_fillMemory(&aHELDeviceDesc,sizeof(aHELDeviceDesc),0); + maDeviceDesc.dwSize = sizeof(maDeviceDesc); + aHELDeviceDesc.dwSize = sizeof(aHELDeviceDesc); + if(FAILED(pDirect3DDevice->GetCaps(&maDeviceDesc,&aHELDeviceDesc))) + { + // go defunct, and exit + VERBOSE_TRACE( "Device::setup3DDevice(): GetCaps() for Direct3DDevice failed" ); + mpDirect3D.reset(); + return false; + } + + mpDirect3DDevice = COMReference< IDirect3DDevice2 >(pDirect3DDevice); + + // select appropriate texture format (_need_ alpha channel here) + rtl_fillMemory( &maTextureFormat, + sizeof(DDPIXELFORMAT), 0 ); + maTextureFormat.dwSize = sizeof(DDPIXELFORMAT); + if( SUCCEEDED(mpDirect3DDevice->EnumTextureFormats( EnumTextureFormatsCallback, &maTextureFormat )) ) + { + bool bSupportedFormat = true; + if((maTextureFormat.dwFlags & (DDPF_ALPHAPIXELS | DDPF_RGB)) != (DDPF_ALPHAPIXELS | DDPF_RGB)) + bSupportedFormat = false; + else if(maTextureFormat.dwRGBAlphaBitMask != 0xFF000000) + bSupportedFormat = false; + else if(maTextureFormat.dwRBitMask != 0x00FF0000) + bSupportedFormat = false; + else if(maTextureFormat.dwGBitMask != 0x0000FF00) + bSupportedFormat = false; + else if(maTextureFormat.dwBBitMask != 0x000000FF) + bSupportedFormat = false; + + if(bSupportedFormat) + { + VERBOSE_TRACE( "Device::setup3DDevice(): chose texture format dwRGBBitCount %d, dwRBitMask %x, " + "dwGBitMask %x, dwBBitMask %x and dwRGBAlphaBitMask %x. The texture uses %s alpha.", + maTextureFormat.dwRGBBitCount, + maTextureFormat.dwRBitMask, + maTextureFormat.dwGBitMask, + maTextureFormat.dwBBitMask, + maTextureFormat.dwRGBAlphaBitMask, + maTextureFormat.dwFlags & DDPF_ALPHAPREMULT ? "premultiplied" : "non-premultiplied" ); + + // setup the device (with as much as we can possibly do here) + // ========================================================== + + LPDIRECT3DVIEWPORT2 pViewport; + + if( SUCCEEDED(mpDirect3D->CreateViewport( &pViewport, NULL )) ) + { + if( SUCCEEDED(mpDirect3DDevice->AddViewport( pViewport )) ) + { + // setup viewport (to whole backbuffer) + D3DVIEWPORT2 aViewport; + + aViewport.dwSize = sizeof(D3DVIEWPORT2); + aViewport.dwX = 0; + aViewport.dwY = 0; + aViewport.dwWidth = maSelectedFullscreenMode.selectedDesc.dwWidth; + aViewport.dwHeight = maSelectedFullscreenMode.selectedDesc.dwHeight; + aViewport.dvClipX = -1.0; + aViewport.dvClipY = -1.0; + aViewport.dvClipWidth = 2.0; + aViewport.dvClipHeight = 2.0; + aViewport.dvMinZ = 0.0; + aViewport.dvMaxZ = 1.0; + + if( SUCCEEDED(pViewport->SetViewport2( &aViewport )) ) + { + if( SUCCEEDED(mpDirect3DDevice->SetCurrentViewport( pViewport )) ) + { + // Viewport was handed over to 3DDevice, thus we can release now + pViewport->Release(); + + // currently, no need for any + // matrix or light source + // setup, since we only render + // transformed&lighted + // vertices + + // done; successfully + return true; + } + else + { + VERBOSE_TRACE( "Device::setup3DDevice(): SetCurrentViewport failed" ); + } + } + else + { + VERBOSE_TRACE( "Device::setup3DDevice(): SetViewport2 failed" ); + } + } + else + { + VERBOSE_TRACE( "Device::setup3DDevice(): AddViewport failed" ); + } + + pViewport->Release(); + } + else + { + VERBOSE_TRACE( "Device::setup3DDevice(): CreateViewport failed" ); + } + } + else + { + VERBOSE_TRACE( "Device::setup3DDevice(): No supported pixelformat" ); + } + } + else + { + VERBOSE_TRACE( "Device::setup3DDevice(): EnumTextureFormats failed" ); + } + + // go defunct, and exit + mpDirect3DDevice.reset(); + mpDirect3D.reset(); + + return false; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::queryCaps + ////////////////////////////////////////////////////////////////////////////////// + + bool DXRenderModule::queryCaps() + { + DDCAPS aHWCaps; + DDCAPS aHELCaps; + + rtl_fillMemory( &aHWCaps, + sizeof(aHWCaps), 0 ); + rtl_fillMemory( &aHELCaps, + sizeof(aHELCaps), 0 ); + aHWCaps.dwSize = sizeof( aHWCaps ); + aHELCaps.dwSize = sizeof( aHELCaps ); + + if( FAILED( mpDirectDraw->GetCaps( &aHWCaps, + &aHELCaps ) ) ) + { + return false; + } + + mbHasNoTearingBlt = aHWCaps.dwFXCaps & DDBLTFX_NOTEARING; + + VERBOSE_TRACE( "dxcanvas initialization: %d bytes VRAM free for surfaces (%d with AGP mem), " + "%d bytes VRAM free for textures (%d with AGP mem)", + getAvailableSurfaceMem( false ), + getAvailableSurfaceMem( true ), + getAvailableTextureMem( false ), + getAvailableTextureMem( true ) ); + + return true; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::validateCaps + ////////////////////////////////////////////////////////////////////////////////// + + bool DXRenderModule::validateCaps() + { + // TODO(E3): Validate HW capabilities. Depending on primary + // surface size, reject HW e.g. on the grounds of insufficient + // VRAM. + + // setup query struct + DDSURFACEDESC desc; + rtl_fillMemory(&desc,sizeof(DDSURFACEDESC),0); + desc.dwSize = sizeof(DDSURFACEDESC); + + // read current display mode, e.g. for screen dimension + if(FAILED( mpDirectDraw->GetDisplayMode(&desc))) + return false; + + // simple heuristic: we need at least 3 times the desktop + // resolution based on ARGB color values... + std::size_t nMinimumVRAMSize = ((desc.dwWidth*desc.dwHeight)<<2)*3; + if(getAvailableSurfaceMem() < nMinimumVRAMSize) + return false; + + return true; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::getDisplayFormat + ////////////////////////////////////////////////////////////////////////////////// + + unsigned int DXRenderModule::getDisplayFormat() const + { + unsigned int nFormat; + nFormat = ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwRGBAlphaBitMask)<<12; + nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwRBitMask)<<8; + nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwGBitMask)<<4; + nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwBBitMask); + return nFormat; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::getAvailableSurfaceMem + ////////////////////////////////////////////////////////////////////////////////// + + std::size_t DXRenderModule::getAvailableSurfaceMem( bool bWithAGPMem ) const + { + if( !mpDirectDraw.is() ) + return 0; + + std::size_t nRes( 0 ); + + DDSCAPS aSurfaceCaps; + DWORD nTotal, nFree; + + // real VRAM (const_cast, since GetAvailableVidMem is non-const) + aSurfaceCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; + if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) ) + return 0; + + nRes += nFree; + + if( bWithAGPMem ) + { + // AGP RAM (const_cast, since GetAvailableVidMem is non-const) + aSurfaceCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; + if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) ) + return 0; + + nRes += nFree; + } + + return nRes; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::getAvailableTextureMem + ////////////////////////////////////////////////////////////////////////////////// + + std::size_t DXRenderModule::getAvailableTextureMem( bool bWithAGPMem ) const + { + if( !mpDirectDraw.is() ) + return 0; + + std::size_t nRes( 0 ); + + DDSCAPS aSurfaceCaps; + DWORD nTotal, nFree; + + // TODO(F1): Check if flags are applicable + + // real VRAM (const_cast, since GetAvailableVidMem is non-const) + aSurfaceCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; + if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) ) + return 0; + + nRes += nFree; + + if( bWithAGPMem ) + { + // AGP RAM (const_cast, since GetAvailableVidMem is non-const) + aSurfaceCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; + if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) ) + return 0; + + nRes += nFree; + } + + // TODO(F1): Add pool mem + + return nRes; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::convert2Screen + ////////////////////////////////////////////////////////////////////////////////// + + void DXRenderModule::convert2Screen( ::basegfx::B2IPoint& io_rDestPos, + ::basegfx::B2IRange& io_rDestArea ) + { + POINT aPoint = { 0, 0 }; + ClientToScreen( mhWnd, &aPoint ); + + // i52230 make sure given screen coordinate is relative to + // this monitor's area (the device rendering is always + // contained to a single monitor) + aPoint.x -= maMonitorInfo.rcMonitor.left; + aPoint.y -= maMonitorInfo.rcMonitor.top; + + io_rDestPos.setX( io_rDestPos.getX() + aPoint.x ); + io_rDestPos.setY( io_rDestPos.getY() + aPoint.y ); + + const ::basegfx::B2ISize& rSize( getFramebufferSize() ); + + // calc output bounds (clip against framebuffer bounds) + io_rDestArea = ::basegfx::B2IRange( + ::std::max( sal_Int32(0), + ::std::min( sal_Int32(rSize.getX()), + sal_Int32(io_rDestArea.getMinX() + aPoint.x) ) ), + ::std::max( sal_Int32(0), + ::std::min( sal_Int32(rSize.getY()), + sal_Int32(io_rDestArea.getMinY() + aPoint.y) ) ), + ::std::max( sal_Int32(0), + ::std::min( sal_Int32(rSize.getX()), + sal_Int32(io_rDestArea.getMaxX() + aPoint.x) ) ), + ::std::max( sal_Int32(0), + ::std::min( sal_Int32(rSize.getY()), + sal_Int32(io_rDestArea.getMaxY() + aPoint.y) ) ) ); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::createSystemMemorySurface + ////////////////////////////////////////////////////////////////////////////////// + + COMReference<IDirectDrawSurface> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize ) + { + DDSURFACEDESC aSurfaceDesc; + IDirectDrawSurface* pSurface; + + aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); + aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;; + aSurfaceDesc.dwWidth = rSize.getX(); + aSurfaceDesc.dwHeight= rSize.getY(); + + rtl_copyMemory( &aSurfaceDesc.ddpfPixelFormat, &maTextureFormat, sizeof(DDPIXELFORMAT) ); + + aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + + HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); + if(FAILED(nRes)) + return COMReference<IDirectDrawSurface>(NULL); + + return COMReference<IDirectDrawSurface>(pSurface); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::flip + ////////////////////////////////////////////////////////////////////////////////// + + bool DXRenderModule::flip( const ::basegfx::B2IRectangle& rUpdateArea, + const ::basegfx::B2IRectangle& rCurrWindowArea ) + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + // see if the main surfaces got lost. if so, try to + // restore them. bail out if this operation fails. + if(!(validateMainSurfaces())) + return false; + + flushVertexCache(); + + ENSURE_OR_THROW( !mnBeginSceneCount, + "Device::flip(): within 3D scene" ); + + // TODO(E3): handle DX errors more thoroughly. For fullscreen + // exclusive mode, actually even our primary surface can get + // lost and needs restore! + + if( mpDirectDraw.is() && + mpPrimarySurface.is() && + mpBackBufferSurface.is() ) + { + // ignore area and offset for page flipping device + if( mbPageFlipping ) + { +#if defined(VERBOSE) && defined(DBG_UTIL) + renderFPSCounter(); + renderMemAvailable(); +#endif + VERBOSE_TRACE( "Device::flip(): Using true page flipping" ); + + // use true page flipping. Hopefully, the 3D hardware + // is flushed on this flip call (rumours have it that + // way), otherwise, perform the Lock hack as for the + // Blt below. + if( SUCCEEDED(mpPrimarySurface->Flip( NULL, DDFLIP_WAIT )) ) + return true; + } + else + { + VERBOSE_TRACE( "Device::flip(): Using blt for page flipping" ); + + // determine actual window position + ::basegfx::B2IPoint aDestPoint( rUpdateArea.getMinimum() ); + ::basegfx::B2IRange aSourceArea( rUpdateArea ); + ::basegfx::B2IRange aDestArea( 0,0, + static_cast<sal_Int32>(rCurrWindowArea.getWidth()), + static_cast<sal_Int32>(rCurrWindowArea.getHeight()) ); + convert2Screen( aDestPoint, aDestArea ); + + // perform clipping + if( !::canvas::tools::clipBlit( aSourceArea, + aDestPoint, + rUpdateArea, + aDestArea ) ) + return true; // fully clipped, but still, in a way, + // successful. + + // TODO(P1): Rumours have it that the 3D hardware + // _might_ still be rendering with flaky drivers, + // which don't flush properly on Blt(). It was said, + // that 'usually', it works to lock the 3D render + // target (the backbuffer in this case). OTOH, I've + // found that this tends to degrade performance + // significantly on complying cards... + + // TODO(P1): Up until rev. 1.3, this method contained + // code to make sure the blit will start _immediately_ + // after the Blt call. If this is not warranted, wait + // for the next vsync. As this case was found to be + // extremely seldom, kicked out (what's more, there's + // simply no guarantee that the blitter will be + // available at any point in the code - Windows still + // is a preemptive multi-processing environment. And + // _if_ we're competing with someone over the blitter, + // we will do so the next VBLANK interval, and the + // following...) + + // screen update seems to be smoother when waiting + // for vblank in every case - even when blitter + // supports the DDBLTFX_NOTEARING flag. + if( FAILED(mpDirectDraw->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, + NULL)) ) + return false; + + DDBLTFX aBltFx; + DDBLTFX* pBltFX = NULL; + if( mbHasNoTearingBlt ) + { + // Blt can internally schedule for no-tearing + // =========================================== + + rtl_fillMemory( &aBltFx, + sizeof(aBltFx), 0 ); + aBltFx.dwSize = sizeof(aBltFx); + aBltFx.dwDDFX = DDBLTFX_NOTEARING; + + pBltFX = &aBltFx; + } + + if( doBlit( aDestPoint, + *mpPrimarySurface, + aSourceArea, + *mpBackBufferSurface, + pBltFX,false ) ) + { +#if defined(VERBOSE) && defined(DBG_UTIL) + renderFPSCounter(); + renderMemAvailable(); +#endif + return true; + } + } + } + return false; + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::disposing + ////////////////////////////////////////////////////////////////////////////////// + + void DXRenderModule::disposing() + { + if(!(mhWnd)) + return; + + mpTexture.reset(); + mpWindow.reset(); + mhWnd=NULL; + + // refrain from releasing the DX5 objects - deleting the + // DX5 device seems to kill the whole engine, including + // all objects we might still hold references to + // (surfaces, e.g.) + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::screenshot + ////////////////////////////////////////////////////////////////////////////////// + + void DXRenderModule::screenShot() + { + if(!(mpBackBufferSurface.get())) + return; + char filename[256]; + static sal_uInt32 counter = 0; + sprintf(filename,"c:\\shot%d.bmp",counter++); + dumpSurface(mpBackBufferSurface,filename); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::validateMainSurfaces + ////////////////////////////////////////////////////////////////////////////////// + + bool DXRenderModule::validateMainSurfaces() + { + if(mpPrimarySurface.get()) { + if(mpPrimarySurface->IsLost() == DDERR_SURFACELOST) { + if(FAILED(mpPrimarySurface->Restore())) + return false; + } + } + + if(mpBackBufferSurface.get()) { + if(mpBackBufferSurface->IsLost() == DDERR_SURFACELOST) + { + // TODO(F1): simply restoring the backbuffer does not + // work as expected, we need to re-create everything + // from scratch. find out why... + //if(SUCCEEDED(mpBackBufferSurface->Restore())) + // return setup3DDevice(); + + mpBackBufferSurface.reset(); + + // get us a backbuffer for simulated flipping + IDirectDrawSurface* pSurface; + + // TODO(P2): Strictly speaking, we don't need a full screen worth of + // backbuffer here. We could also scale dynamically with the current + // window size, but this will make it necessary to temporarily have two + // buffers while copying from the old to the new one. YMMV. + const ::basegfx::B2ISize aSize( getFramebufferSize() ); + + DDSURFACEDESC aSurfaceDesc; + rtl_fillMemory( &aSurfaceDesc, sizeof(DDSURFACEDESC), 0 ); + aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); + aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + aSurfaceDesc.dwHeight= aSize.getY(); + aSurfaceDesc.dwWidth = aSize.getX(); + + aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; + + HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); + + if( FAILED( nRes ) ) + { + if( nRes == DDERR_OUTOFVIDEOMEMORY ) + { + // local vid mem failed. Maybe AGP mem works? + aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; + if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL)) ) + { + // no chance + return false; + } + + VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer reverted to non-local video mem" ); + } + else + { + // no chance + VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" ); + return false; + } + } + + VERBOSE_TRACE( "Device::Device(): created backbuffer of size %d times %d pixel", + aSurfaceDesc.dwWidth, + aSurfaceDesc.dwHeight ); + + mpBackBufferSurface = COMReference< IDirectDrawSurface >(pSurface); + + return setup3DDevice(); + } + } + + return true; + } + + void DXRenderModule::renderInfoText( const ::rtl::OUString& rStr, + const Gdiplus::PointF& rPos ) const + { + ENSURE_OR_THROW( !mnBeginSceneCount, + "Device::renderInfoText(): within 3D scene" ); + + // render text directly to primary surface + GraphicsSharedPtr pGraphics; + + if( mbPageFlipping ) + { + // render on top of backbuffer. We have + // page flipping, anyway, thus this will + // cost us nothing. + pGraphics = createSurfaceGraphics( mpBackBufferSurface ); + } + else + { + // render FPS directly to front buffer. + // That saves us another explicit blit, + // and for me, the FPS counter can blink, + // if it likes to... + pGraphics = createSurfaceGraphics( mpPrimarySurface ); + } + + if( !mbPageFlipping ) + { + // clear background. We might be doing optimized redraws, + // and the background under the FPS count will then not be + // cleared. + Gdiplus::SolidBrush aBrush( + Gdiplus::Color( 255, 255, 255 ) ); + + pGraphics->FillRectangle( &aBrush, + rPos.X, rPos.Y, 80.0, 20.0 ); + } + + Gdiplus::SolidBrush aBrush( + Gdiplus::Color( 255, 0, 255 ) ); + Gdiplus::Font aFont( NULL, + 16, + Gdiplus::FontStyleRegular, + Gdiplus::UnitWorld, + NULL ); + pGraphics->DrawString( reinterpret_cast<LPCWSTR>(rStr.getStr()), + rStr.getLength(), + &aFont, + rPos, + &aBrush ); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::renderMemAvailable + ////////////////////////////////////////////////////////////////////////////////// + + void DXRenderModule::renderMemAvailable() const + { + ENSURE_OR_THROW( !mnBeginSceneCount, + "DXRenderModule::renderMemAvailable(): within 3D scene" ); + + const double nSurfaceMem( getAvailableSurfaceMem()/1024 ); + + ::rtl::OUString text( ::rtl::math::doubleToUString( nSurfaceMem, + rtl_math_StringFormat_F, + 2,'.',NULL,' ') ); + + // pad with leading space + while( text.getLength() < 6 ) + text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; + + text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("S: ")) + text; + + renderInfoText( text, + Gdiplus::PointF( 0.0, 20) ); + + + const double nTexMem( getAvailableTextureMem()/1024 ); + + text = ::rtl::math::doubleToUString( nTexMem, + rtl_math_StringFormat_F, + 2,'.',NULL,' '); + // pad with leading space + while( text.getLength() < 6 ) + text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; + + text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("T: ")) + text; + + renderInfoText( text, + Gdiplus::PointF( 0.0, 40) ); + + VERBOSE_TRACE( "dxcanvas: %f free surface mem, %f free texture mem", + nSurfaceMem, nTexMem ); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::renderFPSCounter + ////////////////////////////////////////////////////////////////////////////////// + + void DXRenderModule::renderFPSCounter() const + { + ENSURE_OR_THROW( !mnBeginSceneCount, + "DXRenderModule::ren derFPSCounter(): within 3D scene" ); + + const double denominator( maLastUpdate.getElapsedTime() ); + maLastUpdate.reset(); + + ::rtl::OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator, + rtl_math_StringFormat_F, + 2,'.',NULL,' ') ); + + // pad with leading space + while( text.getLength() < 6 ) + text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; + + text += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" fps")); + + renderInfoText( text, + Gdiplus::PointF() ); + + VERBOSE_TRACE( "dxcanvas: %f FPS", + denominator == 0.0 ? 100.0 : 1.0/denominator ); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::resize + ////////////////////////////////////////////////////////////////////////////////// + + void DXRenderModule::resize( const ::basegfx::B2IRange& rect ) + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + if( mhWnd==0 ) + return; + + // don't do anything if the size didn't change. + if(maSize.getX() == static_cast<sal_Int32>(rect.getWidth()) && + maSize.getY() == static_cast<sal_Int32>(rect.getHeight())) + return; + + // TODO(Q2): use numeric cast to prevent overflow + maSize.setX(static_cast<sal_Int32>(rect.getWidth())); + maSize.setY(static_cast<sal_Int32>(rect.getHeight())); + + mpWindow->SetPosSizePixel(0,0,maSize.getX(),maSize.getY()); + } + + ////////////////////////////////////////////////////////////////////////////////// + // DXRenderModule::getPageSize + ////////////////////////////////////////////////////////////////////////////////// + + ::basegfx::B2IVector DXRenderModule::getPageSize() + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + return maPageSize; + } + + ::canvas::ISurfaceSharedPtr DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize ) + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + const ::basegfx::B2IVector& rPageSize( getPageSize() ); + ::basegfx::B2ISize aSize(surfaceSize); + if(!(aSize.getX())) + aSize.setX(rPageSize.getX()); + if(!(aSize.getY())) + aSize.setY(rPageSize.getY()); + + if(mpTexture.use_count() == 1) + return mpTexture; + + return ::canvas::ISurfaceSharedPtr( + new DXSurface(*this, + aSize) ); + } + + void DXRenderModule::beginPrimitive( PrimitiveType eType ) + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + ENSURE_OR_THROW( !mnBeginSceneCount, + "DXRenderModule::beginPrimitive(): nested call" ); + + ++mnBeginSceneCount; + meType=eType; + mnCount=0; + } + + void DXRenderModule::endPrimitive() + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + --mnBeginSceneCount; + meType=PRIMITIVE_TYPE_UNKNOWN; + mnCount=0; + } + + void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex ) + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + switch(meType) + { + case PRIMITIVE_TYPE_TRIANGLE: + { + maVertexCache.push_back(vertex); + ++mnCount; + mnCount &= 3; + break; + } + + case PRIMITIVE_TYPE_QUAD: + { + if(mnCount == 3) + { + const std::size_t size(maVertexCache.size()); + ::canvas::Vertex v0(maVertexCache[size-1]); + ::canvas::Vertex v2(maVertexCache[size-3]); + maVertexCache.push_back(v0); + maVertexCache.push_back(vertex); + maVertexCache.push_back(v2); + mnCount=0; + } + else + { + maVertexCache.push_back(vertex); + ++mnCount; + } + break; + } + + default: + OSL_ENSURE( false, + "DXRenderModule::pushVertex(): unexpected primitive types" ); + break; + } + } + + bool DXRenderModule::isError() + { + // TODO(P2): get rid of those fine-grained locking + ::osl::MutexGuard aGuard( maMutex ); + + return mbError; + } + + void DXRenderModule::flushVertexCache() + { + if(!(maVertexCache.size())) + return; + + mbError=true; + + if( FAILED(mpDirect3DDevice->BeginScene()) ) + return; + + // enable texture alpha blending + if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE))) + return; + + // enable texture alpha modulation, for honoring fAlpha + if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND, + D3DTBLEND_MODULATEALPHA)) ) + return; + + // enable texture magnification filtering (don't care if this + // fails, it's just visually more pleasant) + mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAG, + D3DFILTER_LINEAR); + + // enable texture minification filtering (don't care if this + // fails, it's just visually more pleasant) + mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMIN, + D3DFILTER_LINEAR); + + // enable subpixel texture output (don't care if this + // fails, it's just visually more pleasant) + mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_SUBPIXEL, + TRUE); + + // normal combination of object... + if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, + D3DBLEND_SRCALPHA)) ) + return; + + // ..and background color + if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, + D3DBLEND_INVSRCALPHA)) ) + return; + + // disable backface culling; this enables us to mirror sprites + // by simply reverting the triangles, which, with enabled + // culling, would be invisible otherwise + if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, + D3DCULL_NONE)) ) + return; + + mbError=false; + + const float nHalfPixelSizeX(0.5f/maPageSize.getX()); + const float nHalfPixelSizeY(0.5f/maPageSize.getY()); + sal_uInt32 nIndex(0); + const std::size_t size(maVertexCache.size()); + D3DTLVERTEX *vertices = static_cast<D3DTLVERTEX *>(_alloca(sizeof(D3DTLVERTEX)*size)); + vertexCache_t::const_iterator it(maVertexCache.begin()); + while(it != maVertexCache.end()) + { + vertices[nIndex++] = D3DTLVERTEX( + D3DVECTOR(static_cast<D3DVALUE>(it->x), + static_cast<D3DVALUE>(it->y), + static_cast<D3DVALUE>(it->z)), + 1, + D3DRGBA(1,1,1,it->a), + D3DRGBA(0,0,0,0), + static_cast<float>(it->u + nHalfPixelSizeX), + static_cast<float>(it->v + nHalfPixelSizeY)); + ++it; + } + + maVertexCache.clear(); + + mbError |= FAILED(mpDirect3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, + D3DVT_TLVERTEX, + (LPVOID)vertices, + size, + 0)); + + mbError |= FAILED(mpDirect3DDevice->EndScene()); + } + } + + IDXRenderModuleSharedPtr createRenderModule( const ::Window& rParent ) + { + return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) ); + } +} + +#endif |