summaryrefslogtreecommitdiff
path: root/canvas/source/directx/dx_9rm.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'canvas/source/directx/dx_9rm.cxx')
-rwxr-xr-xcanvas/source/directx/dx_9rm.cxx1363
1 files changed, 1363 insertions, 0 deletions
diff --git a/canvas/source/directx/dx_9rm.cxx b/canvas/source/directx/dx_9rm.cxx
new file mode 100755
index 000000000000..a0f485befa12
--- /dev/null
+++ b/canvas/source/directx/dx_9rm.cxx
@@ -0,0 +1,1363 @@
+/*************************************************************************
+ *
+ * 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
+
+#define MAX_TEXTURE_SIZE (2048)
+#define MIN_TEXTURE_SIZE (32)
+//#define FAKE_MAX_NUMBER_TEXTURES (2)
+//#define FAKE_MAX_TEXTURE_SIZE (4096)
+
+#define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
+ // vertex buffer (must be divisable
+ // by 3, as each triangle primitive
+ // has 3 vertices)
+
+
+//////////////////////////////////////////////////////////////////////////////////
+// includes
+//////////////////////////////////////////////////////////////////////////////////
+#include <vcl/syschild.hxx>
+#include <vcl/window.hxx>
+
+#include <canvas/debug.hxx>
+#include <canvas/verbosetrace.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <canvas/elapsedtime.hxx>
+#include <canvas/canvastools.hxx>
+#include <canvas/rendering/icolorbuffer.hxx>
+#include <canvas/rendering/isurface.hxx>
+#include <canvas/rendering/irendermodule.hxx>
+#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>
+
+#include "dx_rendermodule.hxx"
+#include "dx_config.hxx"
+
+#undef WB_LEFT
+#undef WB_RIGHT
+
+#include "dx_impltools.hxx"
+#include <vcl/sysdata.hxx>
+
+#if defined(DX_DEBUG_IMAGES)
+# if OSL_DEBUG_LEVEL > 0
+# include <imdebug.h>
+# undef min
+# undef max
+# endif
+#endif
+
+using namespace ::com::sun::star;
+
+//////////////////////////////////////////////////////////////////////////////////
+// 'dxcanvas' namespace
+//////////////////////////////////////////////////////////////////////////////////
+
+namespace dxcanvas
+{
+ namespace
+ {
+ //////////////////////////////////////////////////////////////////////////////////
+ // monitorSupport
+ //////////////////////////////////////////////////////////////////////////////////
+
+ class monitorSupport
+ {
+ public:
+
+ monitorSupport() :
+ mhLibrary(LoadLibrary("user32.dll")),
+ mpMonitorFromWindow(NULL)
+ {
+ if(mhLibrary)
+ mpMonitorFromWindow = reinterpret_cast<fMonitorFromWindow>(
+ GetProcAddress(
+ mhLibrary,"MonitorFromWindow"));
+ }
+
+ ~monitorSupport()
+ {
+ if(mhLibrary)
+ FreeLibrary(mhLibrary);
+ mhLibrary=0;
+ }
+
+ HMONITOR MonitorFromWindow( HWND hwnd )
+ {
+ // return adapter_default in case something went wrong...
+ if(!(mpMonitorFromWindow))
+ return HMONITOR(0);
+ // MONITOR_DEFAULTTONEAREST
+ const DWORD dwFlags(0x00000002);
+ return mpMonitorFromWindow(hwnd,dwFlags);
+ }
+ private:
+
+ HINSTANCE mhLibrary;
+ typedef HMONITOR (WINAPI *fMonitorFromWindow )( HWND hwnd, DWORD dwFlags );
+ fMonitorFromWindow mpMonitorFromWindow;
+ };
+
+ monitorSupport aMonitorSupport;
+
+
+ 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();
+ COMReference<IDirect3DTexture9> getTexture() const;
+
+ 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<IDirect3DTexture9> mpTexture;
+
+ ::basegfx::B2IVector maSize;
+ };
+
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule
+ //////////////////////////////////////////////////////////////////////////////////
+
+ /// Default implementation of IDXRenderModule
+ class DXRenderModule : public IDXRenderModule
+ {
+ public:
+ explicit DXRenderModule( const ::Window& rWindow );
+ ~DXRenderModule();
+
+ virtual void lock() const { maMutex.acquire(); }
+ virtual void unlock() const { maMutex.release(); }
+
+ virtual COMReference<IDirect3DSurface9>
+ createSystemMemorySurface( const ::basegfx::B2IVector& rSize );
+ virtual void disposing();
+ virtual HWND getHWND() const { return mhWnd; }
+ virtual void screenShot();
+
+ virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
+ const ::basegfx::B2IRectangle& rCurrWindowArea );
+
+ virtual void resize( const ::basegfx::B2IRange& rect );
+ 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();
+
+ COMReference<IDirect3DDevice9> getDevice() { return mpDevice; }
+
+ void flushVertexCache();
+ void commitVertexCache();
+
+ private:
+
+ bool create( const ::Window& rWindow );
+ bool createDevice();
+ bool verifyDevice( const UINT nAdapter );
+ UINT getAdapterFromWindow();
+
+ /** 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;
+ COMReference<IDirect3DDevice9> mpDevice;
+ COMReference<IDirect3D9> mpDirect3D9;
+ COMReference<IDirect3DSwapChain9> mpSwapChain;
+ COMReference<IDirect3DVertexBuffer9> mpVertexBuffer;
+ ::canvas::ISurfaceSharedPtr mpTexture;
+ ::boost::scoped_ptr<SystemChildWindow> mpWindow;
+ ::basegfx::B2IVector maSize;
+ typedef std::vector<canvas::Vertex> vertexCache_t;
+ vertexCache_t maVertexCache;
+ std::size_t mnCount;
+ int mnBeginSceneCount;
+ bool mbCanUseDynamicTextures;
+ bool mbError;
+ PrimitiveType meType;
+ ::basegfx::B2IVector maPageSize;
+ D3DPRESENT_PARAMETERS mad3dpp;
+
+ inline bool isDisposed() const { return (mhWnd==NULL); }
+
+ struct dxvertex
+ {
+ float x,y,z,rhw;
+ DWORD diffuse;
+ float u,v;
+ };
+
+ std::size_t maNumVertices;
+ std::size_t maWriteIndex;
+ std::size_t maReadIndex;
+ };
+
+ ::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),
+ 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");
+
+ COMReference<IDirect3DDevice9> pDevice(rRenderModule.getDevice());
+
+ IDirect3DTexture9 *pTexture(NULL);
+ if(FAILED(pDevice->CreateTexture(
+ rSize.getX(),
+ rSize.getY(),
+ 1,0,D3DFMT_A8R8G8B8,
+ D3DPOOL_MANAGED,
+ &pTexture,NULL)))
+ return;
+
+ mpTexture=COMReference<IDirect3DTexture9>(pTexture);
+ maSize = rSize;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXSurface::~DXSurface
+ //////////////////////////////////////////////////////////////////////////////////
+
+ DXSurface::~DXSurface()
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+
+#ifdef FAKE_MAX_NUMBER_TEXTURES
+ gNumSurfaces--;
+#endif
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXSurface::selectTexture
+ //////////////////////////////////////////////////////////////////////////////////
+
+ bool DXSurface::selectTexture()
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+ mrRenderModule.flushVertexCache();
+ COMReference<IDirect3DDevice9> pDevice(mrRenderModule.getDevice());
+
+ if( FAILED(pDevice->SetTexture(0,mpTexture.get())) )
+ return false;
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXSurface::isValid
+ //////////////////////////////////////////////////////////////////////////////////
+
+ bool DXSurface::isValid()
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+
+ if(!(mpTexture.is()))
+ 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;
+
+ D3DLOCKED_RECT aLockedRect;
+ RECT rect;
+ rect.left = std::max(sal_Int32(0),rDestPos.getX());
+ rect.top = std::max(sal_Int32(0),rDestPos.getY());
+ // to avoid interpolation artifacts from other textures,
+ // the surface manager allocates one pixel gap between
+ // them. Clear that to transparent.
+ rect.right = std::min(maSize.getX(),
+ rect.left + sal_Int32(rSourceRect.getWidth()+1));
+ rect.bottom = std::min(maSize.getY(),
+ rect.top + sal_Int32(rSourceRect.getHeight()+1));
+ const bool bClearRightColumn( rect.right < maSize.getX() );
+ const bool bClearBottomRow( rect.bottom < maSize.getY() );
+
+ if(SUCCEEDED(mpTexture->LockRect(0,&aLockedRect,&rect,D3DLOCK_NOSYSLOCK)))
+ {
+ 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*)aLockedRect.pBits;
+
+ 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);
+
+ if( bClearRightColumn )
+ {
+ // to avoid interpolation artifacts
+ // from other textures, the surface
+ // manager allocates one pixel gap
+ // between them. Clear that to
+ // transparent.
+ pDst[nNumBytesToCopy] =
+ pDst[nNumBytesToCopy+1] =
+ pDst[nNumBytesToCopy+2] =
+ pDst[nNumBytesToCopy+3] = 0x00;
+ }
+ pDst += aLockedRect.Pitch;
+ pImage += nSourcePitchInBytes;
+ }
+
+ if( bClearBottomRow )
+ rtl_zeroMemory(pDst,nNumBytesToCopy+4);
+ }
+ 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*)aLockedRect.pBits;
+
+ const sal_Int32 nNumColumns(
+ sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
+ const sal_Int32 nNumLines(
+ sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
+ for(sal_Int32 i=0; i<nNumLines; ++i)
+ {
+ sal_uInt32 *pDstScanline = reinterpret_cast<sal_uInt32 *>(pDst);
+ sal_uInt8 *pSrcScanline = reinterpret_cast<sal_uInt8 *>(pImage);
+
+ for(sal_Int32 x=0; x<nNumColumns; ++x)
+ {
+ sal_uInt32 color(0xFF000000);
+ color |= pSrcScanline[2]<<16;
+ color |= pSrcScanline[1]<<8;
+ color |= pSrcScanline[0];
+ pSrcScanline += 3;
+ *pDstScanline++ = color;
+ }
+ if( bClearRightColumn )
+ *pDstScanline++ = 0xFF000000;
+
+ pDst += aLockedRect.Pitch;
+ pImage += nSourcePitchInBytes;
+ }
+
+ if( bClearBottomRow )
+ rtl_zeroMemory(pDst,4*(nNumColumns+1));
+ }
+ 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*)aLockedRect.pBits;
+
+ const sal_Int32 nNumLines(
+ sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
+ const sal_Int32 nNumColumns(
+ sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
+ for(sal_Int32 i=0; i<nNumLines; ++i)
+ {
+ sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage);
+ sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst);
+ for(sal_Int32 j=0; j<nNumColumns; ++j)
+ pDst32[j] = 0xFF000000 | pSrc32[j];
+
+ if( bClearRightColumn )
+ pDst32[nNumColumns] = 0xFF000000;
+
+ pDst += aLockedRect.Pitch;
+ pImage += nSourcePitchInBytes;
+ }
+
+ if( bClearBottomRow )
+ rtl_zeroMemory(pDst,4*(nNumColumns+1));
+ }
+ break;
+
+ default:
+ ENSURE_OR_RETURN_FALSE(false,
+ "DXSurface::update(): Unknown/unimplemented buffer format" );
+ break;
+ }
+
+ rSource.unlock();
+ }
+
+ return SUCCEEDED(mpTexture->UnlockRect(0));
+ }
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXSurface::getSize
+ //////////////////////////////////////////////////////////////////////////////////
+
+ ::basegfx::B2IVector DXSurface::getSize()
+ {
+ return maSize;
+ }
+
+ COMReference<IDirect3DTexture9> DXSurface::getTexture() const
+ {
+ return mpTexture;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::DXRenderModule
+ //////////////////////////////////////////////////////////////////////////////////
+
+ DXRenderModule::DXRenderModule( const ::Window& rWindow ) :
+ mhWnd(0),
+ mpDevice(),
+ mpDirect3D9(),
+ mpSwapChain(),
+ mpVertexBuffer(),
+ mpTexture(),
+ maSize(),
+ maVertexCache(),
+ mnCount(0),
+ mnBeginSceneCount(0),
+ mbCanUseDynamicTextures(false),
+ mbError( false ),
+ meType( PRIMITIVE_TYPE_UNKNOWN ),
+ maPageSize(),
+ mad3dpp(),
+ maNumVertices( VERTEX_BUFFER_SIZE ),
+ maWriteIndex(0),
+ maReadIndex(0)
+ {
+ // 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(maPageSize);
+ 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 - "
+ "insufficient texture space!") ),NULL);
+ }
+ }
+ maPageSize=aPageSize;
+
+ IDirect3DVertexBuffer9 *pVB(NULL);
+ DWORD aFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1);
+ if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
+ D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
+ aFVF,
+ D3DPOOL_DEFAULT,
+ &pVB,
+ NULL)) )
+ {
+ throw lang::NoSupportException(
+ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
+ "Could not create DirectX device - out of memory!")),NULL);
+ }
+
+ mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::~DXRenderModule
+ //////////////////////////////////////////////////////////////////////////////////
+
+ DXRenderModule::~DXRenderModule()
+ {
+ disposing();
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::disposing
+ //////////////////////////////////////////////////////////////////////////////////
+
+ void DXRenderModule::disposing()
+ {
+ if(!(mhWnd))
+ return;
+
+ mpTexture.reset();
+ mpWindow.reset();
+ mhWnd=NULL;
+
+ // refrain from releasing the DX9 objects. We're the only
+ // ones holding references to them, and it might be
+ // dangerous to destroy the DX9 device, before all other
+ // objects are dead.
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // 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) ),
+ "DXRenderModule::create() 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());
+
+ // TODO(F2): since we would like to share precious hardware
+ // resources, the direct3d9 object should be global. each new
+ // request for a canvas should only create a new swapchain.
+ mpDirect3D9 = COMReference<IDirect3D9>(
+ Direct3DCreate9(D3D_SDK_VERSION));
+ if(!mpDirect3D9.is())
+ return false;
+
+ // create a device from the direct3d9 object.
+ if(!(createDevice()))
+ return false;
+
+ mpWindow->Show();
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::verifyDevice
+ //////////////////////////////////////////////////////////////////////////////////
+
+ bool DXRenderModule::verifyDevice( const UINT nAdapter )
+ {
+ ENSURE_OR_THROW( mpDirect3D9.is(),
+ "DXRenderModule::verifyDevice() No valid device." );
+
+ // ask direct3d9 about the capabilities of hardware devices on a specific adapter.
+ // here we decide if the underlying hardware of the machine 'is good enough'.
+ // since we only need a tiny little fraction of what could be used, this
+ // is basically a no-op.
+ D3DCAPS9 aCaps;
+ if(FAILED(mpDirect3D9->GetDeviceCaps(nAdapter,D3DDEVTYPE_HAL,&aCaps)))
+ return false;
+ if(!(aCaps.MaxTextureWidth))
+ return false;
+ if(!(aCaps.MaxTextureHeight))
+ return false;
+ maPageSize = ::basegfx::B2IVector(aCaps.MaxTextureWidth,aCaps.MaxTextureHeight);
+
+ // check device against white & blacklist entries
+ D3DADAPTER_IDENTIFIER9 aIdent;
+ if(FAILED(mpDirect3D9->GetAdapterIdentifier(nAdapter,0,&aIdent)))
+ return false;
+
+ DXCanvasItem aConfigItem;
+ DXCanvasItem::DeviceInfo aInfo;
+ aInfo.nVendorId = aIdent.VendorId;
+ aInfo.nDeviceId = aIdent.DeviceId;
+ aInfo.nDeviceSubSysId = aIdent.SubSysId;
+ aInfo.nDeviceRevision = aIdent.Revision;
+
+ aInfo.nDriverId = HIWORD(aIdent.DriverVersion.HighPart);
+ aInfo.nDriverVersion = LOWORD(aIdent.DriverVersion.HighPart);
+ aInfo.nDriverSubVersion = HIWORD(aIdent.DriverVersion.LowPart);
+ aInfo.nDriverBuildId = LOWORD(aIdent.DriverVersion.LowPart);
+
+ if( !aConfigItem.isDeviceUsable(aInfo) )
+ return false;
+
+ if( aConfigItem.isBlacklistCurrentDevice() )
+ {
+ aConfigItem.blacklistDevice(aInfo);
+ return false;
+ }
+
+ aConfigItem.adaptMaxTextureSize(maPageSize);
+
+ mbCanUseDynamicTextures = (aCaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0;
+
+ return true;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::createDevice
+ //////////////////////////////////////////////////////////////////////////////////
+
+ bool DXRenderModule::createDevice()
+ {
+ // we expect that the caller provides us with a valid HWND
+ ENSURE_OR_THROW( IsWindow(mhWnd),
+ "DXRenderModule::createDevice() No valid HWND given." );
+
+ // we expect that the caller already created the direct3d9 object.
+ ENSURE_OR_THROW( mpDirect3D9.is(),
+ "DXRenderModule::createDevice() no direct3d?." );
+
+ // find the adapter identifier from the window.
+ const UINT aAdapter(getAdapterFromWindow());
+ if(aAdapter == static_cast<UINT>(-1))
+ return false;
+
+ // verify that device possibly works
+ if( !verifyDevice(aAdapter) )
+ return false;
+
+ // query the display mode from the selected adapter.
+ // we'll later request the backbuffer format to be same
+ // same as the display format.
+ D3DDISPLAYMODE d3ddm;
+ mpDirect3D9->GetAdapterDisplayMode(aAdapter,&d3ddm);
+
+ // we need to use D3DSWAPEFFECT_COPY here since the canvas-api has
+ // basically nothing to do with efficient resource handling. it tries
+ // to avoid drawing whenevery possible, which is simply not the most
+ // efficient way we could leverage the hardware in this case. it would
+ // be far better to redraw the backbuffer each time we would like to
+ // display the content of the backbuffer, but we need to face reality
+ // here and follow how the canvas was designed.
+
+ // 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.
+ ZeroMemory( &mad3dpp, sizeof(mad3dpp) );
+ mad3dpp.BackBufferWidth = std::max(sal_Int32(maSize.getX()),
+ sal_Int32(d3ddm.Width));
+ mad3dpp.BackBufferHeight = std::max(sal_Int32(maSize.getY()),
+ sal_Int32(d3ddm.Height));
+ mad3dpp.BackBufferCount = 1;
+ mad3dpp.Windowed = TRUE;
+ mad3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
+ mad3dpp.BackBufferFormat = d3ddm.Format;
+ mad3dpp.EnableAutoDepthStencil = FALSE;
+ mad3dpp.hDeviceWindow = mhWnd;
+ mad3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
+
+ // now create the device, first try hardware vertex processing,
+ // then software vertex processing. if both queries fail, we give up
+ // and indicate failure.
+ IDirect3DDevice9 *pDevice(NULL);
+ if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
+ D3DDEVTYPE_HAL,
+ mhWnd,
+ D3DCREATE_HARDWARE_VERTEXPROCESSING|
+ D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
+ &mad3dpp,
+ &pDevice)))
+ if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
+ D3DDEVTYPE_HAL,
+ mhWnd,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING|
+ D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
+ &mad3dpp,
+ &pDevice)))
+ return false;
+
+ // got it, store it in a safe place...
+ mpDevice=COMReference<IDirect3DDevice9>(pDevice);
+
+ // After CreateDevice, the first swap chain already exists, so just get it...
+ IDirect3DSwapChain9 *pSwapChain(NULL);
+ pDevice->GetSwapChain(0,&pSwapChain);
+ mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
+ if( !mpSwapChain.is() )
+ return false;
+
+ // clear the render target [which is the backbuffer in this case].
+ // we are forced to do this once, and furthermore right now.
+ // please note that this is only possible since we created the
+ // backbuffer with copy semantics [the content is preserved after
+ // calls to Present()], which is an unnecessarily expensive operation.
+ LPDIRECT3DSURFACE9 pBackBuffer = NULL;
+ mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
+ mpDevice->SetRenderTarget( 0, pBackBuffer );
+ mpDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0L);
+ pBackBuffer->Release();
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::createSystemMemorySurface
+ //////////////////////////////////////////////////////////////////////////////////
+
+ COMReference<IDirect3DSurface9> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
+ {
+ if(isDisposed())
+ return COMReference<IDirect3DSurface9>(NULL);
+
+ // please note that D3DFMT_X8R8G8B8 is the only format we're
+ // able to choose here, since GetDC() doesn't support any
+ // other 32bit-format.
+ IDirect3DSurface9 *pSurface(NULL);
+ if( FAILED(mpDevice->CreateOffscreenPlainSurface(
+ rSize.getX(),
+ rSize.getY(),
+ D3DFMT_X8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ &pSurface,
+ NULL)) )
+ {
+ throw lang::NoSupportException(
+ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
+ "Could not create offscreen surface - out of mem!") ),NULL);
+ }
+
+ return COMReference<IDirect3DSurface9>(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 );
+
+ if(isDisposed() || !mpSwapChain.is())
+ return false;
+
+ flushVertexCache();
+
+ // TODO(P2): Might be faster to actually pass update area here
+ RECT aRect =
+ {
+ rUpdateArea.getMinX(),
+ rUpdateArea.getMinY(),
+ rUpdateArea.getMaxX(),
+ rUpdateArea.getMaxY()
+ };
+ HRESULT hr(mpSwapChain->Present(&aRect,&aRect,NULL,NULL,0));
+ if(FAILED(hr))
+ {
+ if(hr != D3DERR_DEVICELOST)
+ return false;
+
+ // interestingly enough, sometimes the Reset() below
+ // *still* causes DeviceLost errors. So, cycle until
+ // DX was kind enough to really reset the device...
+ do
+ {
+ mpVertexBuffer.reset();
+ hr = mpDevice->Reset(&mad3dpp);
+ if(SUCCEEDED(hr))
+ {
+ IDirect3DVertexBuffer9 *pVB(NULL);
+ DWORD aFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1);
+ if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
+ D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
+ aFVF,
+ D3DPOOL_DEFAULT,
+ &pVB,
+ NULL)) )
+ {
+ throw lang::NoSupportException(
+ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
+ "Could not create DirectX device - out of memory!")),NULL);
+ }
+ mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
+
+ // retry after the restore
+ if(SUCCEEDED(mpSwapChain->Present(&aRect,&aRect,NULL,NULL,0)))
+ return true;
+ }
+
+ TimeValue aTimeout;
+ aTimeout.Seconds=1;
+ aTimeout.Nanosec=0;
+ osl_waitThread(&aTimeout);
+ }
+ while(hr == D3DERR_DEVICELOST);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::screenShot
+ //////////////////////////////////////////////////////////////////////////////////
+
+ void DXRenderModule::screenShot()
+ {
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::resize
+ //////////////////////////////////////////////////////////////////////////////////
+
+ void DXRenderModule::resize( const ::basegfx::B2IRange& rect )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ 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());
+
+ // resize back buffer, if necessary
+ // -------------------------------------------------------------
+
+ // don't attempt to create anything if the
+ // requested size is NULL.
+ if(!(maSize.getX()))
+ return;
+ if(!(maSize.getY()))
+ return;
+
+ // backbuffer too small (might happen, if window is
+ // maximized across multiple monitors)
+ if( sal_Int32(mad3dpp.BackBufferWidth) < maSize.getX() ||
+ sal_Int32(mad3dpp.BackBufferHeight) < maSize.getY() )
+ {
+ mad3dpp.BackBufferWidth = maSize.getX();
+ mad3dpp.BackBufferHeight = maSize.getY();
+
+ // clear before, save resources
+ mpSwapChain.reset();
+
+ IDirect3DSwapChain9 *pSwapChain(NULL);
+ if(FAILED(mpDevice->CreateAdditionalSwapChain(&mad3dpp,&pSwapChain)))
+ return;
+ mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
+
+ // clear the render target [which is the backbuffer in this case].
+ // we are forced to do this once, and furthermore right now.
+ // please note that this is only possible since we created the
+ // backbuffer with copy semantics [the content is preserved after
+ // calls to Present()], which is an unnecessarily expensive operation.
+ LPDIRECT3DSURFACE9 pBackBuffer = NULL;
+ mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
+ mpDevice->SetRenderTarget( 0, pBackBuffer );
+ mpDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0L);
+ pBackBuffer->Release();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::getPageSize
+ //////////////////////////////////////////////////////////////////////////////////
+
+ ::basegfx::B2IVector DXRenderModule::getPageSize()
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+ return maPageSize;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::createSurface
+ //////////////////////////////////////////////////////////////////////////////////
+
+ ::canvas::ISurfaceSharedPtr DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return ::canvas::ISurfaceSharedPtr();
+
+ 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) );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::beginPrimitive
+ //////////////////////////////////////////////////////////////////////////////////
+
+ void DXRenderModule::beginPrimitive( PrimitiveType eType )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return;
+
+ ENSURE_OR_THROW( !mnBeginSceneCount,
+ "DXRenderModule::beginPrimitive(): nested call" );
+
+ ++mnBeginSceneCount;
+ meType=eType;
+ mnCount=0;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::endPrimitive
+ //////////////////////////////////////////////////////////////////////////////////
+
+ void DXRenderModule::endPrimitive()
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return;
+
+ --mnBeginSceneCount;
+ meType=PRIMITIVE_TYPE_UNKNOWN;
+ mnCount=0;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::pushVertex
+ //////////////////////////////////////////////////////////////////////////////////
+
+ void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return;
+
+ 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 type");
+ break;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::isError
+ //////////////////////////////////////////////////////////////////////////////////
+
+ bool DXRenderModule::isError()
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ return mbError;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::getAdapterFromWindow
+ //////////////////////////////////////////////////////////////////////////////////
+
+ UINT DXRenderModule::getAdapterFromWindow()
+ {
+ HMONITOR hMonitor(aMonitorSupport.MonitorFromWindow(mhWnd));
+ UINT aAdapterCount(mpDirect3D9->GetAdapterCount());
+ for(UINT i=0; i<aAdapterCount; ++i)
+ if(hMonitor == mpDirect3D9->GetAdapterMonitor(i))
+ return i;
+ return static_cast<UINT>(-1);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::commitVertexCache
+ //////////////////////////////////////////////////////////////////////////////////
+
+ void DXRenderModule::commitVertexCache()
+ {
+ if(maReadIndex != maWriteIndex)
+ {
+ const std::size_t nVertexStride = sizeof(dxvertex);
+ const unsigned int nNumVertices = maWriteIndex-maReadIndex;
+ const unsigned int nNumPrimitives = nNumVertices / 3;
+
+ if(FAILED(mpDevice->SetStreamSource(0,mpVertexBuffer.get(),0,nVertexStride)))
+ return;
+
+ if(FAILED(mpDevice->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)))
+ return;
+
+ if(FAILED(mpDevice->BeginScene()))
+ return;
+
+ mbError |= FAILED(mpDevice->DrawPrimitive(D3DPT_TRIANGLELIST,maReadIndex,nNumPrimitives));
+ mbError |= FAILED(mpDevice->EndScene());
+
+ maReadIndex += nNumVertices;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // DXRenderModule::flushVertexCache
+ //////////////////////////////////////////////////////////////////////////////////
+
+ void DXRenderModule::flushVertexCache()
+ {
+ if(!(maVertexCache.size()))
+ return;
+
+ mbError=true;
+
+ if( FAILED(mpDevice->SetRenderState(D3DRS_LIGHTING,FALSE)))
+ return;
+
+ // enable texture alpha blending
+ if( FAILED(mpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE)))
+ return;
+
+ mpDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
+ mpDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
+ mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSU ,D3DTADDRESS_CLAMP );
+ mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSV ,D3DTADDRESS_CLAMP );
+
+ // configure the fixed-function pipeline.
+ // the only 'feature' we need here is to modulate the alpha-channels
+ // from the texture and the interpolated diffuse color. the result
+ // will then be blended with the backbuffer.
+ // fragment color = texture color * diffuse.alpha.
+ mpDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
+ mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
+ mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
+
+ // normal combination of object...
+ if( FAILED(mpDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA)) )
+ return;
+
+ // ..and background color
+ if( FAILED(mpDevice->SetRenderState(D3DRS_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(mpDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE)) )
+ return;
+
+ mbError=false;
+
+ std::size_t nSize(maVertexCache.size());
+ const std::size_t nVertexStride = sizeof(dxvertex);
+
+ const ::basegfx::B2IVector aPageSize(getPageSize());
+ const float nHalfPixelSizeX(0.5f/aPageSize.getX());
+ const float nHalfPixelSizeY(0.5f/aPageSize.getY());
+ vertexCache_t::const_iterator it(maVertexCache.begin());
+
+ while( nSize )
+ {
+ DWORD dwLockFlags(D3DLOCK_NOOVERWRITE);
+
+ // Check to see if there's space for the current set of
+ // vertices in the buffer.
+ if( maNumVertices - maWriteIndex < nSize )
+ {
+ commitVertexCache();
+ dwLockFlags = D3DLOCK_DISCARD;
+ maWriteIndex = 0;
+ maReadIndex = 0;
+ }
+
+ dxvertex *vertices(NULL);
+ const std::size_t nNumVertices(
+ std::min(maNumVertices - maWriteIndex,
+ nSize));
+ if(FAILED(mpVertexBuffer->Lock(maWriteIndex*nVertexStride,
+ nNumVertices*nVertexStride,
+ (void **)&vertices,
+ dwLockFlags)))
+ return;
+
+ std::size_t nIndex(0);
+ while( nIndex < nNumVertices )
+ {
+ dxvertex &dest = vertices[nIndex++];
+ dest.x=it->x;
+ dest.y=it->y;
+ dest.z=it->z;
+ dest.rhw=1;
+ const sal_uInt32 alpha(static_cast<sal_uInt32>(it->a*255.0f));
+ dest.diffuse=D3DCOLOR_ARGB(alpha,255,255,255);
+ dest.u=static_cast<float>(it->u + nHalfPixelSizeX);
+ dest.v=static_cast<float>(it->v + nHalfPixelSizeY);
+ ++it;
+ }
+
+ mpVertexBuffer->Unlock();
+
+ // Advance to the next position in the vertex buffer.
+ maWriteIndex += nNumVertices;
+ nSize -= nNumVertices;
+
+ commitVertexCache();
+ }
+
+ maVertexCache.clear();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // createRenderModule
+ //////////////////////////////////////////////////////////////////////////////////
+
+ IDXRenderModuleSharedPtr createRenderModule( const ::Window& rParent )
+ {
+ return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) );
+ }
+}
+
+#endif