| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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( sal_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( sal_False ); |
| |
| mpWindow->SetControlForeground(); |
| mpWindow->SetControlBackground(); |
| mpWindow->EnablePaint(sal_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 |