1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dx_9rm.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_canvas.hxx"
34 #if DIRECTX_VERSION == 0x0900
36 #define MAX_TEXTURE_SIZE (2048)
37 #define MIN_TEXTURE_SIZE (32)
38 //#define FAKE_MAX_NUMBER_TEXTURES (2)
39 //#define FAKE_MAX_TEXTURE_SIZE (4096)
41 #define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
42 // vertex buffer (must be divisable
43 // by 3, as each triangle primitive
47 //////////////////////////////////////////////////////////////////////////////////
49 //////////////////////////////////////////////////////////////////////////////////
50 #include <vcl/syschild.hxx>
51 #include <vcl/window.hxx>
53 #include <canvas/debug.hxx>
54 #include <canvas/verbosetrace.hxx>
55 #include <tools/diagnose_ex.h>
56 #include <osl/thread.h>
58 #include <canvas/elapsedtime.hxx>
59 #include <canvas/canvastools.hxx>
60 #include <canvas/rendering/icolorbuffer.hxx>
61 #include <canvas/rendering/isurface.hxx>
62 #include <canvas/rendering/irendermodule.hxx>
63 #include <basegfx/numeric/ftools.hxx>
64 #include <basegfx/vector/b2dsize.hxx>
65 #include <basegfx/vector/b2isize.hxx>
66 #include <basegfx/point/b2ipoint.hxx>
67 #include <basegfx/range/b2irectangle.hxx>
68 #include <boost/scoped_ptr.hpp>
69 #include <com/sun/star/lang/NoSupportException.hpp>
71 #include "dx_rendermodule.hxx"
72 #include "dx_config.hxx"
77 #include "dx_impltools.hxx"
78 #include <vcl/sysdata.hxx>
80 #if defined(DX_DEBUG_IMAGES)
81 # if OSL_DEBUG_LEVEL > 0
88 using namespace ::com::sun::star
;
90 //////////////////////////////////////////////////////////////////////////////////
91 // 'dxcanvas' namespace
92 //////////////////////////////////////////////////////////////////////////////////
98 //////////////////////////////////////////////////////////////////////////////////
100 //////////////////////////////////////////////////////////////////////////////////
107 mhLibrary(LoadLibrary("user32.dll")),
108 mpMonitorFromWindow(NULL
)
111 mpMonitorFromWindow
= reinterpret_cast<fMonitorFromWindow
>(
113 mhLibrary
,"MonitorFromWindow"));
119 FreeLibrary(mhLibrary
);
123 HMONITOR
MonitorFromWindow( HWND hwnd
)
125 // return adapter_default in case something went wrong...
126 if(!(mpMonitorFromWindow
))
128 // MONITOR_DEFAULTTONEAREST
129 const DWORD
dwFlags(0x00000002);
130 return mpMonitorFromWindow(hwnd
,dwFlags
);
135 typedef HMONITOR (WINAPI
*fMonitorFromWindow
)( HWND hwnd
, DWORD dwFlags
);
136 fMonitorFromWindow mpMonitorFromWindow
;
139 monitorSupport aMonitorSupport
;
142 class DXRenderModule
;
144 //////////////////////////////////////////////////////////////////////////////////
146 //////////////////////////////////////////////////////////////////////////////////
148 /** ISurface implemenation.
150 @attention holds the DXRenderModule via non-refcounted
151 reference! This is safe with current state of affairs, since
152 the canvas::PageManager holds surface and render module via
153 shared_ptr (and makes sure all surfaces are deleted before its
154 render module member goes out of scope).
156 class DXSurface
: public canvas::ISurface
159 DXSurface( DXRenderModule
& rRenderModule
,
160 const ::basegfx::B2ISize
& rSize
);
163 virtual bool selectTexture();
164 virtual bool isValid();
165 virtual bool update( const ::basegfx::B2IPoint
& rDestPos
,
166 const ::basegfx::B2IRange
& rSourceRect
,
167 ::canvas::IColorBuffer
& rSource
);
168 virtual ::basegfx::B2IVector
getSize();
169 COMReference
<IDirect3DTexture9
> getTexture() const;
172 /// Guard local methods against concurrent acces to RenderModule
173 class ImplRenderModuleGuard
: private ::boost::noncopyable
176 explicit inline ImplRenderModuleGuard( DXRenderModule
& rRenderModule
);
177 inline ~ImplRenderModuleGuard();
180 DXRenderModule
& mrRenderModule
;
183 DXRenderModule
& mrRenderModule
;
184 COMReference
<IDirect3DTexture9
> mpTexture
;
186 ::basegfx::B2IVector maSize
;
190 //////////////////////////////////////////////////////////////////////////////////
192 //////////////////////////////////////////////////////////////////////////////////
194 /// Default implementation of IDXRenderModule
195 class DXRenderModule
: public IDXRenderModule
198 explicit DXRenderModule( const ::Window
& rWindow
);
201 virtual void lock() const { maMutex
.acquire(); }
202 virtual void unlock() const { maMutex
.release(); }
204 virtual COMReference
<IDirect3DSurface9
>
205 createSystemMemorySurface( const ::basegfx::B2IVector
& rSize
);
206 virtual void disposing();
207 virtual HWND
getHWND() const { return mhWnd
; }
208 virtual void screenShot();
210 virtual bool flip( const ::basegfx::B2IRectangle
& rUpdateArea
,
211 const ::basegfx::B2IRectangle
& rCurrWindowArea
);
213 virtual void resize( const ::basegfx::B2IRange
& rect
);
214 virtual ::basegfx::B2IVector
getPageSize();
215 virtual ::canvas::ISurfaceSharedPtr
createSurface( const ::basegfx::B2IVector
& surfaceSize
);
216 virtual void beginPrimitive( PrimitiveType eType
);
217 virtual void endPrimitive();
218 virtual void pushVertex( const ::canvas::Vertex
& vertex
);
219 virtual bool isError();
221 COMReference
<IDirect3DDevice9
> getDevice() { return mpDevice
; }
223 void flushVertexCache();
224 void commitVertexCache();
228 bool create( const ::Window
& rWindow
);
230 bool verifyDevice( const UINT nAdapter
);
231 UINT
getAdapterFromWindow();
233 /** This object represents the DirectX state machine. In order
234 to serialize access to DirectX's global state, a global
237 static ::osl::Mutex maMutex
;
240 COMReference
<IDirect3DDevice9
> mpDevice
;
241 COMReference
<IDirect3D9
> mpDirect3D9
;
242 COMReference
<IDirect3DSwapChain9
> mpSwapChain
;
243 COMReference
<IDirect3DVertexBuffer9
> mpVertexBuffer
;
244 ::canvas::ISurfaceSharedPtr mpTexture
;
245 ::boost::scoped_ptr
<SystemChildWindow
> mpWindow
;
246 ::basegfx::B2IVector maSize
;
247 typedef std::vector
<canvas::Vertex
> vertexCache_t
;
248 vertexCache_t maVertexCache
;
250 int mnBeginSceneCount
;
251 bool mbCanUseDynamicTextures
;
253 PrimitiveType meType
;
254 ::basegfx::B2IVector maPageSize
;
255 D3DPRESENT_PARAMETERS mad3dpp
;
257 inline bool isDisposed() const { return (mhWnd
==NULL
); }
266 std::size_t maNumVertices
;
267 std::size_t maWriteIndex
;
268 std::size_t maReadIndex
;
271 ::osl::Mutex
DXRenderModule::maMutex
;
273 //////////////////////////////////////////////////////////////////////////////////
274 // DXSurface::ImplRenderModuleGuard
275 //////////////////////////////////////////////////////////////////////////////////
277 inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
278 DXRenderModule
& rRenderModule
) :
279 mrRenderModule( rRenderModule
)
281 mrRenderModule
.lock();
284 inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
286 mrRenderModule
.unlock();
289 #ifdef FAKE_MAX_NUMBER_TEXTURES
290 static sal_uInt32 gNumSurfaces
= 0;
293 void fillRect( sal_uInt32
*pDest
,
299 for(sal_uInt32 i
=0; i
<dwWidth
; ++i
)
302 pDest
[((dwHeight
-1)*dwPitch
)+i
]=dwColor
;
305 for(sal_uInt32 j
=0; j
<dwHeight
; ++j
)
308 pDest
[dwWidth
-1]=dwColor
;
313 //////////////////////////////////////////////////////////////////////////////////
314 // DXSurface::DXSurface
315 //////////////////////////////////////////////////////////////////////////////////
317 DXSurface::DXSurface( DXRenderModule
& rRenderModule
,
318 const ::basegfx::B2ISize
& rSize
) :
319 mrRenderModule(rRenderModule
),
323 ImplRenderModuleGuard
aGuard( mrRenderModule
);
325 #ifdef FAKE_MAX_NUMBER_TEXTURES
327 if(gNumSurfaces
>= FAKE_MAX_NUMBER_TEXTURES
)
331 #ifdef FAKE_MAX_TEXTURE_SIZE
332 if(rSize
.getX() > FAKE_MAX_TEXTURE_SIZE
)
334 if(rSize
.getY() > FAKE_MAX_TEXTURE_SIZE
)
338 ENSURE_ARG_OR_THROW(rSize
.getX() > 0 && rSize
.getY() > 0,
339 "DXSurface::DXSurface(): request for zero-sized surface");
341 COMReference
<IDirect3DDevice9
> pDevice(rRenderModule
.getDevice());
343 IDirect3DTexture9
*pTexture(NULL
);
344 if(FAILED(pDevice
->CreateTexture(
352 mpTexture
=COMReference
<IDirect3DTexture9
>(pTexture
);
356 //////////////////////////////////////////////////////////////////////////////////
357 // DXSurface::~DXSurface
358 //////////////////////////////////////////////////////////////////////////////////
360 DXSurface::~DXSurface()
362 ImplRenderModuleGuard
aGuard( mrRenderModule
);
364 #ifdef FAKE_MAX_NUMBER_TEXTURES
369 //////////////////////////////////////////////////////////////////////////////////
370 // DXSurface::selectTexture
371 //////////////////////////////////////////////////////////////////////////////////
373 bool DXSurface::selectTexture()
375 ImplRenderModuleGuard
aGuard( mrRenderModule
);
376 mrRenderModule
.flushVertexCache();
377 COMReference
<IDirect3DDevice9
> pDevice(mrRenderModule
.getDevice());
379 if( FAILED(pDevice
->SetTexture(0,mpTexture
.get())) )
385 //////////////////////////////////////////////////////////////////////////////////
386 // DXSurface::isValid
387 //////////////////////////////////////////////////////////////////////////////////
389 bool DXSurface::isValid()
391 ImplRenderModuleGuard
aGuard( mrRenderModule
);
393 if(!(mpTexture
.is()))
398 //////////////////////////////////////////////////////////////////////////////////
400 //////////////////////////////////////////////////////////////////////////////////
402 bool DXSurface::update( const ::basegfx::B2IPoint
& rDestPos
,
403 const ::basegfx::B2IRange
& rSourceRect
,
404 ::canvas::IColorBuffer
& rSource
)
406 ImplRenderModuleGuard
aGuard( mrRenderModule
);
408 // can't update if surface is not valid, that means
409 // either not existent nor restored...
413 D3DLOCKED_RECT aLockedRect
;
415 rect
.left
= std::max(sal_Int32(0),rDestPos
.getX());
416 rect
.top
= std::max(sal_Int32(0),rDestPos
.getY());
417 // to avoid interpolation artifacts from other textures,
418 // the surface manager allocates one pixel gap between
419 // them. Clear that to transparent.
420 rect
.right
= std::min(maSize
.getX(),
421 rect
.left
+ sal_Int32(rSourceRect
.getWidth()+1));
422 rect
.bottom
= std::min(maSize
.getY(),
423 rect
.top
+ sal_Int32(rSourceRect
.getHeight()+1));
424 const bool bClearRightColumn( rect
.right
< maSize
.getX() );
425 const bool bClearBottomRow( rect
.bottom
< maSize
.getY() );
427 if(SUCCEEDED(mpTexture
->LockRect(0,&aLockedRect
,&rect
,D3DLOCK_NOSYSLOCK
)))
429 if(sal_uInt8
* pImage
= rSource
.lock())
431 switch( rSource
.getFormat() )
433 case ::canvas::IColorBuffer::FMT_A8R8G8B8
:
435 const std::size_t nSourceBytesPerPixel(4);
436 const std::size_t nSourcePitchInBytes(rSource
.getStride());
437 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
438 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
440 // calculate the destination memory address
441 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
443 const sal_uInt32
nNumBytesToCopy(
444 static_cast<sal_uInt32
>(
445 rSourceRect
.getWidth())*
446 nSourceBytesPerPixel
);
447 const sal_uInt64
nNumLines(rSourceRect
.getHeight());
449 for(sal_uInt32 i
=0; i
<nNumLines
; ++i
)
451 rtl_copyMemory(pDst
,pImage
,nNumBytesToCopy
);
453 if( bClearRightColumn
)
455 // to avoid interpolation artifacts
456 // from other textures, the surface
457 // manager allocates one pixel gap
458 // between them. Clear that to
460 pDst
[nNumBytesToCopy
] =
461 pDst
[nNumBytesToCopy
+1] =
462 pDst
[nNumBytesToCopy
+2] =
463 pDst
[nNumBytesToCopy
+3] = 0x00;
465 pDst
+= aLockedRect
.Pitch
;
466 pImage
+= nSourcePitchInBytes
;
469 if( bClearBottomRow
)
470 rtl_zeroMemory(pDst
,nNumBytesToCopy
+4);
474 case ::canvas::IColorBuffer::FMT_R8G8B8
:
476 const std::size_t nSourceBytesPerPixel(3);
477 const std::size_t nSourcePitchInBytes(rSource
.getStride());
478 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
479 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
481 // calculate the destination memory address
482 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
484 const sal_Int32
nNumColumns(
485 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getWidth()));
486 const sal_Int32
nNumLines(
487 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getHeight()));
488 for(sal_Int32 i
=0; i
<nNumLines
; ++i
)
490 sal_uInt32
*pDstScanline
= reinterpret_cast<sal_uInt32
*>(pDst
);
491 sal_uInt8
*pSrcScanline
= reinterpret_cast<sal_uInt8
*>(pImage
);
493 for(sal_Int32 x
=0; x
<nNumColumns
; ++x
)
495 sal_uInt32
color(0xFF000000);
496 color
|= pSrcScanline
[2]<<16;
497 color
|= pSrcScanline
[1]<<8;
498 color
|= pSrcScanline
[0];
500 *pDstScanline
++ = color
;
502 if( bClearRightColumn
)
503 *pDstScanline
++ = 0xFF000000;
505 pDst
+= aLockedRect
.Pitch
;
506 pImage
+= nSourcePitchInBytes
;
509 if( bClearBottomRow
)
510 rtl_zeroMemory(pDst
,4*(nNumColumns
+1));
514 case ::canvas::IColorBuffer::FMT_X8R8G8B8
:
516 const std::size_t nSourceBytesPerPixel(4);
517 const std::size_t nSourcePitchInBytes(rSource
.getStride());
518 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
519 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
521 // calculate the destination memory address
522 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
524 const sal_Int32
nNumLines(
525 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getHeight()));
526 const sal_Int32
nNumColumns(
527 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getWidth()));
528 for(sal_Int32 i
=0; i
<nNumLines
; ++i
)
530 sal_uInt32
*pSrc32
= reinterpret_cast<sal_uInt32
*>(pImage
);
531 sal_uInt32
*pDst32
= reinterpret_cast<sal_uInt32
*>(pDst
);
532 for(sal_Int32 j
=0; j
<nNumColumns
; ++j
)
533 pDst32
[j
] = 0xFF000000 | pSrc32
[j
];
535 if( bClearRightColumn
)
536 pDst32
[nNumColumns
] = 0xFF000000;
538 pDst
+= aLockedRect
.Pitch
;
539 pImage
+= nSourcePitchInBytes
;
542 if( bClearBottomRow
)
543 rtl_zeroMemory(pDst
,4*(nNumColumns
+1));
548 ENSURE_OR_RETURN(false,
549 "DXSurface::update(): Unknown/unimplemented buffer format" );
556 return SUCCEEDED(mpTexture
->UnlockRect(0));
562 //////////////////////////////////////////////////////////////////////////////////
563 // DXSurface::getSize
564 //////////////////////////////////////////////////////////////////////////////////
566 ::basegfx::B2IVector
DXSurface::getSize()
571 COMReference
<IDirect3DTexture9
> DXSurface::getTexture() const
576 //////////////////////////////////////////////////////////////////////////////////
577 // DXRenderModule::DXRenderModule
578 //////////////////////////////////////////////////////////////////////////////////
580 DXRenderModule::DXRenderModule( const ::Window
& rWindow
) :
590 mnBeginSceneCount(0),
591 mbCanUseDynamicTextures(false),
593 meType( PRIMITIVE_TYPE_UNKNOWN
),
596 maNumVertices( VERTEX_BUFFER_SIZE
),
600 // TODO(P2): get rid of those fine-grained locking
601 ::osl::MutexGuard
aGuard( maMutex
);
603 if(!(create(rWindow
)))
605 throw lang::NoSupportException(
606 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
607 "Could not create DirectX device!") ),NULL
);
610 // allocate a single texture surface which can be used later.
611 // we also use this to calibrate the page size.
612 ::basegfx::B2IVector
aPageSize(maPageSize
);
615 mpTexture
= ::canvas::ISurfaceSharedPtr(
616 new DXSurface(*this,aPageSize
));
617 if(mpTexture
->isValid())
620 aPageSize
.setX(aPageSize
.getX()>>1);
621 aPageSize
.setY(aPageSize
.getY()>>1);
622 if((aPageSize
.getX() < MIN_TEXTURE_SIZE
) ||
623 (aPageSize
.getY() < MIN_TEXTURE_SIZE
))
625 throw lang::NoSupportException(
626 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
627 "Could not create DirectX device - "
628 "insufficient texture space!") ),NULL
);
631 maPageSize
=aPageSize
;
633 IDirect3DVertexBuffer9
*pVB(NULL
);
634 DWORD
aFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
);
635 if( FAILED(mpDevice
->CreateVertexBuffer(sizeof(dxvertex
)*maNumVertices
,
636 D3DUSAGE_DYNAMIC
|D3DUSAGE_WRITEONLY
,
642 throw lang::NoSupportException(
643 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
644 "Could not create DirectX device - out of memory!")),NULL
);
647 mpVertexBuffer
=COMReference
<IDirect3DVertexBuffer9
>(pVB
);
650 //////////////////////////////////////////////////////////////////////////////////
651 // DXRenderModule::~DXRenderModule
652 //////////////////////////////////////////////////////////////////////////////////
654 DXRenderModule::~DXRenderModule()
659 //////////////////////////////////////////////////////////////////////////////////
660 // DXRenderModule::disposing
661 //////////////////////////////////////////////////////////////////////////////////
663 void DXRenderModule::disposing()
672 // refrain from releasing the DX9 objects. We're the only
673 // ones holding references to them, and it might be
674 // dangerous to destroy the DX9 device, before all other
678 //////////////////////////////////////////////////////////////////////////////////
679 // DXRenderModule::create
680 //////////////////////////////////////////////////////////////////////////////////
682 bool DXRenderModule::create( const ::Window
& rWindow
)
684 // TODO(P2): get rid of those fine-grained locking
685 ::osl::MutexGuard
aGuard( maMutex
);
687 maVertexCache
.reserve(1024);
690 new SystemChildWindow(
691 const_cast<Window
*>(&rWindow
), 0) );
693 // system child window must not receive mouse events
694 mpWindow
->SetMouseTransparent( TRUE
);
696 // parent should receive paint messages as well
697 // [PARENTCLIPMODE_NOCLIP], the argument is here
698 // passed as plain numeric value since the stupid
699 // define utilizes a USHORT cast.
700 mpWindow
->SetParentClipMode(0x0002);
702 // the system child window must not clear its background
703 mpWindow
->EnableEraseBackground( FALSE
);
705 mpWindow
->SetControlForeground();
706 mpWindow
->SetControlBackground();
707 mpWindow
->EnablePaint(FALSE
);
709 const SystemEnvData
*pData
= mpWindow
->GetSystemData();
710 const HWND
hwnd(reinterpret_cast<HWND
>(pData
->hWnd
));
711 mhWnd
= const_cast<HWND
>(hwnd
);
713 ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND
>(mhWnd
) ),
714 "DXRenderModule::create() No valid HWND given." );
716 // retrieve position and size of the parent window
717 const ::Size
&rSizePixel(rWindow
.GetSizePixel());
719 // remember the size of the parent window, since we
720 // need to use this for our child window.
721 maSize
.setX(static_cast<sal_Int32
>(rSizePixel
.Width()));
722 maSize
.setY(static_cast<sal_Int32
>(rSizePixel
.Height()));
724 // let the child window cover the same size as the parent window.
725 mpWindow
->SetPosSizePixel(0,0,maSize
.getX(),maSize
.getY());
727 // TODO(F2): since we would like to share precious hardware
728 // resources, the direct3d9 object should be global. each new
729 // request for a canvas should only create a new swapchain.
730 mpDirect3D9
= COMReference
<IDirect3D9
>(
731 Direct3DCreate9(D3D_SDK_VERSION
));
732 if(!mpDirect3D9
.is())
735 // create a device from the direct3d9 object.
736 if(!(createDevice()))
744 //////////////////////////////////////////////////////////////////////////////////
745 // DXRenderModule::verifyDevice
746 //////////////////////////////////////////////////////////////////////////////////
748 bool DXRenderModule::verifyDevice( const UINT nAdapter
)
750 ENSURE_OR_THROW( mpDirect3D9
.is(),
751 "DXRenderModule::verifyDevice() No valid device." );
753 // ask direct3d9 about the capabilities of hardware devices on a specific adapter.
754 // here we decide if the underlying hardware of the machine 'is good enough'.
755 // since we only need a tiny little fraction of what could be used, this
756 // is basically a no-op.
758 if(FAILED(mpDirect3D9
->GetDeviceCaps(nAdapter
,D3DDEVTYPE_HAL
,&aCaps
)))
760 if(!(aCaps
.MaxTextureWidth
))
762 if(!(aCaps
.MaxTextureHeight
))
764 maPageSize
= ::basegfx::B2IVector(aCaps
.MaxTextureWidth
,aCaps
.MaxTextureHeight
);
766 // check device against white & blacklist entries
767 D3DADAPTER_IDENTIFIER9 aIdent
;
768 if(FAILED(mpDirect3D9
->GetAdapterIdentifier(nAdapter
,0,&aIdent
)))
771 DXCanvasItem aConfigItem
;
772 DXCanvasItem::DeviceInfo aInfo
;
773 aInfo
.nVendorId
= aIdent
.VendorId
;
774 aInfo
.nDeviceId
= aIdent
.DeviceId
;
775 aInfo
.nDeviceSubSysId
= aIdent
.SubSysId
;
776 aInfo
.nDeviceRevision
= aIdent
.Revision
;
778 aInfo
.nDriverId
= HIWORD(aIdent
.DriverVersion
.HighPart
);
779 aInfo
.nDriverVersion
= LOWORD(aIdent
.DriverVersion
.HighPart
);
780 aInfo
.nDriverSubVersion
= HIWORD(aIdent
.DriverVersion
.LowPart
);
781 aInfo
.nDriverBuildId
= LOWORD(aIdent
.DriverVersion
.LowPart
);
783 if( !aConfigItem
.isDeviceUsable(aInfo
) )
786 if( aConfigItem
.isBlacklistCurrentDevice() )
788 aConfigItem
.blacklistDevice(aInfo
);
792 aConfigItem
.adaptMaxTextureSize(maPageSize
);
794 mbCanUseDynamicTextures
= (aCaps
.Caps2
& D3DCAPS2_DYNAMICTEXTURES
) != 0;
800 //////////////////////////////////////////////////////////////////////////////////
801 // DXRenderModule::createDevice
802 //////////////////////////////////////////////////////////////////////////////////
804 bool DXRenderModule::createDevice()
806 // we expect that the caller provides us with a valid HWND
807 ENSURE_OR_THROW( IsWindow(mhWnd
),
808 "DXRenderModule::createDevice() No valid HWND given." );
810 // we expect that the caller already created the direct3d9 object.
811 ENSURE_OR_THROW( mpDirect3D9
.is(),
812 "DXRenderModule::createDevice() no direct3d?." );
814 // find the adapter identifier from the window.
815 const UINT
aAdapter(getAdapterFromWindow());
816 if(aAdapter
== static_cast<UINT
>(-1))
819 // verify that device possibly works
820 if( !verifyDevice(aAdapter
) )
823 // query the display mode from the selected adapter.
824 // we'll later request the backbuffer format to be same
825 // same as the display format.
826 D3DDISPLAYMODE d3ddm
;
827 mpDirect3D9
->GetAdapterDisplayMode(aAdapter
,&d3ddm
);
829 // we need to use D3DSWAPEFFECT_COPY here since the canvas-api has
830 // basically nothing to do with efficient resource handling. it tries
831 // to avoid drawing whenevery possible, which is simply not the most
832 // efficient way we could leverage the hardware in this case. it would
833 // be far better to redraw the backbuffer each time we would like to
834 // display the content of the backbuffer, but we need to face reality
835 // here and follow how the canvas was designed.
837 // Strictly speaking, we don't need a full screen worth of
838 // backbuffer here. We could also scale dynamically with
839 // the current window size, but this will make it
840 // necessary to temporarily have two buffers while copying
841 // from the old to the new one. What's more, at the time
842 // we need a larger buffer, DX might not have sufficient
843 // resources available, and we're then left with too small
844 // a back buffer, and no way of falling back to a
845 // different canvas implementation.
846 ZeroMemory( &mad3dpp
, sizeof(mad3dpp
) );
847 mad3dpp
.BackBufferWidth
= std::max(sal_Int32(maSize
.getX()),
848 sal_Int32(d3ddm
.Width
));
849 mad3dpp
.BackBufferHeight
= std::max(sal_Int32(maSize
.getY()),
850 sal_Int32(d3ddm
.Height
));
851 mad3dpp
.BackBufferCount
= 1;
852 mad3dpp
.Windowed
= TRUE
;
853 mad3dpp
.SwapEffect
= D3DSWAPEFFECT_COPY
;
854 mad3dpp
.BackBufferFormat
= d3ddm
.Format
;
855 mad3dpp
.EnableAutoDepthStencil
= FALSE
;
856 mad3dpp
.hDeviceWindow
= mhWnd
;
857 mad3dpp
.PresentationInterval
= D3DPRESENT_INTERVAL_ONE
;
859 // now create the device, first try hardware vertex processing,
860 // then software vertex processing. if both queries fail, we give up
861 // and indicate failure.
862 IDirect3DDevice9
*pDevice(NULL
);
863 if(FAILED(mpDirect3D9
->CreateDevice(aAdapter
,
866 D3DCREATE_HARDWARE_VERTEXPROCESSING
|
867 D3DCREATE_MULTITHREADED
|D3DCREATE_FPU_PRESERVE
,
870 if(FAILED(mpDirect3D9
->CreateDevice(aAdapter
,
873 D3DCREATE_SOFTWARE_VERTEXPROCESSING
|
874 D3DCREATE_MULTITHREADED
|D3DCREATE_FPU_PRESERVE
,
879 // got it, store it in a safe place...
880 mpDevice
=COMReference
<IDirect3DDevice9
>(pDevice
);
882 // After CreateDevice, the first swap chain already exists, so just get it...
883 IDirect3DSwapChain9
*pSwapChain(NULL
);
884 pDevice
->GetSwapChain(0,&pSwapChain
);
885 mpSwapChain
=COMReference
<IDirect3DSwapChain9
>(pSwapChain
);
886 if( !mpSwapChain
.is() )
889 // clear the render target [which is the backbuffer in this case].
890 // we are forced to do this once, and furthermore right now.
891 // please note that this is only possible since we created the
892 // backbuffer with copy semantics [the content is preserved after
893 // calls to Present()], which is an unnecessarily expensive operation.
894 LPDIRECT3DSURFACE9 pBackBuffer
= NULL
;
895 mpSwapChain
->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO
,&pBackBuffer
);
896 mpDevice
->SetRenderTarget( 0, pBackBuffer
);
897 mpDevice
->Clear(0,NULL
,D3DCLEAR_TARGET
,0,1.0f
,0L);
898 pBackBuffer
->Release();
903 //////////////////////////////////////////////////////////////////////////////////
904 // DXRenderModule::createSystemMemorySurface
905 //////////////////////////////////////////////////////////////////////////////////
907 COMReference
<IDirect3DSurface9
> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector
& rSize
)
910 return COMReference
<IDirect3DSurface9
>(NULL
);
912 // please note that D3DFMT_X8R8G8B8 is the only format we're
913 // able to choose here, since GetDC() doesn't support any
914 // other 32bit-format.
915 IDirect3DSurface9
*pSurface(NULL
);
916 if( FAILED(mpDevice
->CreateOffscreenPlainSurface(
924 throw lang::NoSupportException(
925 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
926 "Could not create offscreen surface - out of mem!") ),NULL
);
929 return COMReference
<IDirect3DSurface9
>(pSurface
);
932 //////////////////////////////////////////////////////////////////////////////////
933 // DXRenderModule::flip
934 //////////////////////////////////////////////////////////////////////////////////
936 bool DXRenderModule::flip( const ::basegfx::B2IRectangle
& rUpdateArea
,
937 const ::basegfx::B2IRectangle
& /*rCurrWindowArea*/ )
939 // TODO(P2): get rid of those fine-grained locking
940 ::osl::MutexGuard
aGuard( maMutex
);
942 if(isDisposed() || !mpSwapChain
.is())
947 // TODO(P2): Might be faster to actually pass update area here
950 rUpdateArea
.getMinX(),
951 rUpdateArea
.getMinY(),
952 rUpdateArea
.getMaxX(),
953 rUpdateArea
.getMaxY()
955 HRESULT
hr(mpSwapChain
->Present(&aRect
,&aRect
,NULL
,NULL
,0));
958 if(hr
!= D3DERR_DEVICELOST
)
961 // interestingly enough, sometimes the Reset() below
962 // *still* causes DeviceLost errors. So, cycle until
963 // DX was kind enough to really reset the device...
966 mpVertexBuffer
.reset();
967 hr
= mpDevice
->Reset(&mad3dpp
);
970 IDirect3DVertexBuffer9
*pVB(NULL
);
971 DWORD
aFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
);
972 if( FAILED(mpDevice
->CreateVertexBuffer(sizeof(dxvertex
)*maNumVertices
,
973 D3DUSAGE_DYNAMIC
|D3DUSAGE_WRITEONLY
,
979 throw lang::NoSupportException(
980 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
981 "Could not create DirectX device - out of memory!")),NULL
);
983 mpVertexBuffer
=COMReference
<IDirect3DVertexBuffer9
>(pVB
);
985 // retry after the restore
986 if(SUCCEEDED(mpSwapChain
->Present(&aRect
,&aRect
,NULL
,NULL
,0)))
993 osl_waitThread(&aTimeout
);
995 while(hr
== D3DERR_DEVICELOST
);
1003 //////////////////////////////////////////////////////////////////////////////////
1004 // DXRenderModule::screenShot
1005 //////////////////////////////////////////////////////////////////////////////////
1007 void DXRenderModule::screenShot()
1011 //////////////////////////////////////////////////////////////////////////////////
1012 // DXRenderModule::resize
1013 //////////////////////////////////////////////////////////////////////////////////
1015 void DXRenderModule::resize( const ::basegfx::B2IRange
& rect
)
1017 // TODO(P2): get rid of those fine-grained locking
1018 ::osl::MutexGuard
aGuard( maMutex
);
1023 // don't do anything if the size didn't change.
1024 if(maSize
.getX() == static_cast<sal_Int32
>(rect
.getWidth()) &&
1025 maSize
.getY() == static_cast<sal_Int32
>(rect
.getHeight()))
1028 // TODO(Q2): use numeric cast to prevent overflow
1029 maSize
.setX(static_cast<sal_Int32
>(rect
.getWidth()));
1030 maSize
.setY(static_cast<sal_Int32
>(rect
.getHeight()));
1032 mpWindow
->SetPosSizePixel(0,0,maSize
.getX(),maSize
.getY());
1034 // resize back buffer, if necessary
1035 // -------------------------------------------------------------
1037 // don't attempt to create anything if the
1038 // requested size is NULL.
1039 if(!(maSize
.getX()))
1041 if(!(maSize
.getY()))
1044 // backbuffer too small (might happen, if window is
1045 // maximized across multiple monitors)
1046 if( sal_Int32(mad3dpp
.BackBufferWidth
) < maSize
.getX() ||
1047 sal_Int32(mad3dpp
.BackBufferHeight
) < maSize
.getY() )
1049 mad3dpp
.BackBufferWidth
= maSize
.getX();
1050 mad3dpp
.BackBufferHeight
= maSize
.getY();
1052 // clear before, save resources
1053 mpSwapChain
.reset();
1055 IDirect3DSwapChain9
*pSwapChain(NULL
);
1056 if(FAILED(mpDevice
->CreateAdditionalSwapChain(&mad3dpp
,&pSwapChain
)))
1058 mpSwapChain
=COMReference
<IDirect3DSwapChain9
>(pSwapChain
);
1060 // clear the render target [which is the backbuffer in this case].
1061 // we are forced to do this once, and furthermore right now.
1062 // please note that this is only possible since we created the
1063 // backbuffer with copy semantics [the content is preserved after
1064 // calls to Present()], which is an unnecessarily expensive operation.
1065 LPDIRECT3DSURFACE9 pBackBuffer
= NULL
;
1066 mpSwapChain
->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO
,&pBackBuffer
);
1067 mpDevice
->SetRenderTarget( 0, pBackBuffer
);
1068 mpDevice
->Clear(0,NULL
,D3DCLEAR_TARGET
,0,1.0f
,0L);
1069 pBackBuffer
->Release();
1073 //////////////////////////////////////////////////////////////////////////////////
1074 // DXRenderModule::getPageSize
1075 //////////////////////////////////////////////////////////////////////////////////
1077 ::basegfx::B2IVector
DXRenderModule::getPageSize()
1079 // TODO(P2): get rid of those fine-grained locking
1080 ::osl::MutexGuard
aGuard( maMutex
);
1084 //////////////////////////////////////////////////////////////////////////////////
1085 // DXRenderModule::createSurface
1086 //////////////////////////////////////////////////////////////////////////////////
1088 ::canvas::ISurfaceSharedPtr
DXRenderModule::createSurface( const ::basegfx::B2IVector
& surfaceSize
)
1090 // TODO(P2): get rid of those fine-grained locking
1091 ::osl::MutexGuard
aGuard( maMutex
);
1094 return ::canvas::ISurfaceSharedPtr();
1096 const ::basegfx::B2IVector
& rPageSize( getPageSize() );
1097 ::basegfx::B2ISize
aSize(surfaceSize
);
1099 aSize
.setX(rPageSize
.getX());
1101 aSize
.setY(rPageSize
.getY());
1103 if(mpTexture
.use_count() == 1)
1106 return ::canvas::ISurfaceSharedPtr( new DXSurface(*this,aSize
) );
1109 //////////////////////////////////////////////////////////////////////////////////
1110 // DXRenderModule::beginPrimitive
1111 //////////////////////////////////////////////////////////////////////////////////
1113 void DXRenderModule::beginPrimitive( PrimitiveType eType
)
1115 // TODO(P2): get rid of those fine-grained locking
1116 ::osl::MutexGuard
aGuard( maMutex
);
1121 ENSURE_OR_THROW( !mnBeginSceneCount
,
1122 "DXRenderModule::beginPrimitive(): nested call" );
1124 ++mnBeginSceneCount
;
1129 //////////////////////////////////////////////////////////////////////////////////
1130 // DXRenderModule::endPrimitive
1131 //////////////////////////////////////////////////////////////////////////////////
1133 void DXRenderModule::endPrimitive()
1135 // TODO(P2): get rid of those fine-grained locking
1136 ::osl::MutexGuard
aGuard( maMutex
);
1141 --mnBeginSceneCount
;
1142 meType
=PRIMITIVE_TYPE_UNKNOWN
;
1146 //////////////////////////////////////////////////////////////////////////////////
1147 // DXRenderModule::pushVertex
1148 //////////////////////////////////////////////////////////////////////////////////
1150 void DXRenderModule::pushVertex( const ::canvas::Vertex
& vertex
)
1152 // TODO(P2): get rid of those fine-grained locking
1153 ::osl::MutexGuard
aGuard( maMutex
);
1160 case PRIMITIVE_TYPE_TRIANGLE
:
1162 maVertexCache
.push_back(vertex
);
1168 case PRIMITIVE_TYPE_QUAD
:
1172 const std::size_t size(maVertexCache
.size());
1173 ::canvas::Vertex
v0(maVertexCache
[size
-1]);
1174 ::canvas::Vertex
v2(maVertexCache
[size
-3]);
1175 maVertexCache
.push_back(v0
);
1176 maVertexCache
.push_back(vertex
);
1177 maVertexCache
.push_back(v2
);
1182 maVertexCache
.push_back(vertex
);
1190 "DXRenderModule::pushVertex(): unexpected primitive type");
1195 //////////////////////////////////////////////////////////////////////////////////
1196 // DXRenderModule::isError
1197 //////////////////////////////////////////////////////////////////////////////////
1199 bool DXRenderModule::isError()
1201 // TODO(P2): get rid of those fine-grained locking
1202 ::osl::MutexGuard
aGuard( maMutex
);
1207 //////////////////////////////////////////////////////////////////////////////////
1208 // DXRenderModule::getAdapterFromWindow
1209 //////////////////////////////////////////////////////////////////////////////////
1211 UINT
DXRenderModule::getAdapterFromWindow()
1213 HMONITOR
hMonitor(aMonitorSupport
.MonitorFromWindow(mhWnd
));
1214 UINT
aAdapterCount(mpDirect3D9
->GetAdapterCount());
1215 for(UINT i
=0; i
<aAdapterCount
; ++i
)
1216 if(hMonitor
== mpDirect3D9
->GetAdapterMonitor(i
))
1218 return static_cast<UINT
>(-1);
1221 //////////////////////////////////////////////////////////////////////////////////
1222 // DXRenderModule::commitVertexCache
1223 //////////////////////////////////////////////////////////////////////////////////
1225 void DXRenderModule::commitVertexCache()
1227 if(maReadIndex
!= maWriteIndex
)
1229 const std::size_t nVertexStride
= sizeof(dxvertex
);
1230 const unsigned int nNumVertices
= maWriteIndex
-maReadIndex
;
1231 const unsigned int nNumPrimitives
= nNumVertices
/ 3;
1233 if(FAILED(mpDevice
->SetStreamSource(0,mpVertexBuffer
.get(),0,nVertexStride
)))
1236 if(FAILED(mpDevice
->SetFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
)))
1239 if(FAILED(mpDevice
->BeginScene()))
1242 mbError
|= FAILED(mpDevice
->DrawPrimitive(D3DPT_TRIANGLELIST
,maReadIndex
,nNumPrimitives
));
1243 mbError
|= FAILED(mpDevice
->EndScene());
1245 maReadIndex
+= nNumVertices
;
1249 //////////////////////////////////////////////////////////////////////////////////
1250 // DXRenderModule::flushVertexCache
1251 //////////////////////////////////////////////////////////////////////////////////
1253 void DXRenderModule::flushVertexCache()
1255 if(!(maVertexCache
.size()))
1260 if( FAILED(mpDevice
->SetRenderState(D3DRS_LIGHTING
,FALSE
)))
1263 // enable texture alpha blending
1264 if( FAILED(mpDevice
->SetRenderState(D3DRS_ALPHABLENDENABLE
,TRUE
)))
1267 mpDevice
->SetSamplerState(0,D3DSAMP_MAGFILTER
,D3DTEXF_LINEAR
);
1268 mpDevice
->SetSamplerState(0,D3DSAMP_MINFILTER
,D3DTEXF_LINEAR
);
1269 mpDevice
->SetSamplerState(0,D3DSAMP_ADDRESSU
,D3DTADDRESS_CLAMP
);
1270 mpDevice
->SetSamplerState(0,D3DSAMP_ADDRESSV
,D3DTADDRESS_CLAMP
);
1272 // configure the fixed-function pipeline.
1273 // the only 'feature' we need here is to modulate the alpha-channels
1274 // from the texture and the interpolated diffuse color. the result
1275 // will then be blended with the backbuffer.
1276 // fragment color = texture color * diffuse.alpha.
1277 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAOP
,D3DTOP_MODULATE
);
1278 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAARG1
,D3DTA_TEXTURE
);
1279 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAARG2
,D3DTA_DIFFUSE
);
1281 // normal combination of object...
1282 if( FAILED(mpDevice
->SetRenderState(D3DRS_SRCBLEND
,D3DBLEND_SRCALPHA
)) )
1285 // ..and background color
1286 if( FAILED(mpDevice
->SetRenderState(D3DRS_DESTBLEND
,D3DBLEND_INVSRCALPHA
)) )
1289 // disable backface culling; this enables us to mirror sprites
1290 // by simply reverting the triangles, which, with enabled
1291 // culling, would be invisible otherwise
1292 if( FAILED(mpDevice
->SetRenderState(D3DRS_CULLMODE
,D3DCULL_NONE
)) )
1297 std::size_t nSize(maVertexCache
.size());
1298 const std::size_t nVertexStride
= sizeof(dxvertex
);
1300 const ::basegfx::B2IVector
aPageSize(getPageSize());
1301 const float nHalfPixelSizeX(0.5f
/aPageSize
.getX());
1302 const float nHalfPixelSizeY(0.5f
/aPageSize
.getY());
1303 vertexCache_t::const_iterator
it(maVertexCache
.begin());
1307 DWORD
dwLockFlags(D3DLOCK_NOOVERWRITE
);
1309 // Check to see if there's space for the current set of
1310 // vertices in the buffer.
1311 if( maNumVertices
- maWriteIndex
< nSize
)
1313 commitVertexCache();
1314 dwLockFlags
= D3DLOCK_DISCARD
;
1319 dxvertex
*vertices(NULL
);
1320 const std::size_t nNumVertices(
1321 std::min(maNumVertices
- maWriteIndex
,
1323 if(FAILED(mpVertexBuffer
->Lock(maWriteIndex
*nVertexStride
,
1324 nNumVertices
*nVertexStride
,
1329 std::size_t nIndex(0);
1330 while( nIndex
< nNumVertices
)
1332 dxvertex
&dest
= vertices
[nIndex
++];
1337 const sal_uInt32
alpha(static_cast<sal_uInt32
>(it
->a
*255.0f
));
1338 dest
.diffuse
=D3DCOLOR_ARGB(alpha
,255,255,255);
1339 dest
.u
=static_cast<float>(it
->u
+ nHalfPixelSizeX
);
1340 dest
.v
=static_cast<float>(it
->v
+ nHalfPixelSizeY
);
1344 mpVertexBuffer
->Unlock();
1346 // Advance to the next position in the vertex buffer.
1347 maWriteIndex
+= nNumVertices
;
1348 nSize
-= nNumVertices
;
1350 commitVertexCache();
1353 maVertexCache
.clear();
1357 //////////////////////////////////////////////////////////////////////////////////
1358 // createRenderModule
1359 //////////////////////////////////////////////////////////////////////////////////
1361 IDXRenderModuleSharedPtr
createRenderModule( const ::Window
& rParent
)
1363 return IDXRenderModuleSharedPtr( new DXRenderModule(rParent
) );