1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_canvas.hxx"
31 #if DIRECTX_VERSION == 0x0900
33 #define MAX_TEXTURE_SIZE (2048)
34 #define MIN_TEXTURE_SIZE (32)
35 //#define FAKE_MAX_NUMBER_TEXTURES (2)
36 //#define FAKE_MAX_TEXTURE_SIZE (4096)
38 #define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
39 // vertex buffer (must be divisable
40 // by 3, as each triangle primitive
44 //////////////////////////////////////////////////////////////////////////////////
46 //////////////////////////////////////////////////////////////////////////////////
47 #include <vcl/syschild.hxx>
48 #include <vcl/window.hxx>
50 #include <canvas/debug.hxx>
51 #include <canvas/verbosetrace.hxx>
52 #include <tools/diagnose_ex.h>
54 #include <canvas/elapsedtime.hxx>
55 #include <canvas/canvastools.hxx>
56 #include <canvas/rendering/icolorbuffer.hxx>
57 #include <canvas/rendering/isurface.hxx>
58 #include <canvas/rendering/irendermodule.hxx>
59 #include <basegfx/numeric/ftools.hxx>
60 #include <basegfx/vector/b2dsize.hxx>
61 #include <basegfx/vector/b2isize.hxx>
62 #include <basegfx/point/b2ipoint.hxx>
63 #include <basegfx/range/b2irectangle.hxx>
64 #include <boost/scoped_ptr.hpp>
65 #include <com/sun/star/lang/NoSupportException.hpp>
67 #include "dx_rendermodule.hxx"
68 #include "dx_config.hxx"
73 #include "dx_impltools.hxx"
74 #include <vcl/sysdata.hxx>
76 #if defined(DX_DEBUG_IMAGES)
77 # if OSL_DEBUG_LEVEL > 0
84 using namespace ::com::sun::star
;
86 //////////////////////////////////////////////////////////////////////////////////
87 // 'dxcanvas' namespace
88 //////////////////////////////////////////////////////////////////////////////////
94 //////////////////////////////////////////////////////////////////////////////////
96 //////////////////////////////////////////////////////////////////////////////////
103 mhLibrary(LoadLibrary("user32.dll")),
104 mpMonitorFromWindow(NULL
)
107 mpMonitorFromWindow
= reinterpret_cast<fMonitorFromWindow
>(
109 mhLibrary
,"MonitorFromWindow"));
115 FreeLibrary(mhLibrary
);
119 HMONITOR
MonitorFromWindow( HWND hwnd
)
121 // return adapter_default in case something went wrong...
122 if(!(mpMonitorFromWindow
))
124 // MONITOR_DEFAULTTONEAREST
125 const DWORD
dwFlags(0x00000002);
126 return mpMonitorFromWindow(hwnd
,dwFlags
);
131 typedef HMONITOR (WINAPI
*fMonitorFromWindow
)( HWND hwnd
, DWORD dwFlags
);
132 fMonitorFromWindow mpMonitorFromWindow
;
135 monitorSupport aMonitorSupport
;
138 class DXRenderModule
;
140 //////////////////////////////////////////////////////////////////////////////////
142 //////////////////////////////////////////////////////////////////////////////////
144 /** ISurface implemenation.
146 @attention holds the DXRenderModule via non-refcounted
147 reference! This is safe with current state of affairs, since
148 the canvas::PageManager holds surface and render module via
149 shared_ptr (and makes sure all surfaces are deleted before its
150 render module member goes out of scope).
152 class DXSurface
: public canvas::ISurface
155 DXSurface( DXRenderModule
& rRenderModule
,
156 const ::basegfx::B2ISize
& rSize
);
159 virtual bool selectTexture();
160 virtual bool isValid();
161 virtual bool update( const ::basegfx::B2IPoint
& rDestPos
,
162 const ::basegfx::B2IRange
& rSourceRect
,
163 ::canvas::IColorBuffer
& rSource
);
164 virtual ::basegfx::B2IVector
getSize();
165 COMReference
<IDirect3DTexture9
> getTexture() const;
168 /// Guard local methods against concurrent acces to RenderModule
169 class ImplRenderModuleGuard
: private ::boost::noncopyable
172 explicit inline ImplRenderModuleGuard( DXRenderModule
& rRenderModule
);
173 inline ~ImplRenderModuleGuard();
176 DXRenderModule
& mrRenderModule
;
179 DXRenderModule
& mrRenderModule
;
180 COMReference
<IDirect3DTexture9
> mpTexture
;
182 ::basegfx::B2IVector maSize
;
186 //////////////////////////////////////////////////////////////////////////////////
188 //////////////////////////////////////////////////////////////////////////////////
190 /// Default implementation of IDXRenderModule
191 class DXRenderModule
: public IDXRenderModule
194 explicit DXRenderModule( const ::Window
& rWindow
);
197 virtual void lock() const { maMutex
.acquire(); }
198 virtual void unlock() const { maMutex
.release(); }
200 virtual COMReference
<IDirect3DSurface9
>
201 createSystemMemorySurface( const ::basegfx::B2IVector
& rSize
);
202 virtual void disposing();
203 virtual HWND
getHWND() const { return mhWnd
; }
204 virtual void screenShot();
206 virtual bool flip( const ::basegfx::B2IRectangle
& rUpdateArea
,
207 const ::basegfx::B2IRectangle
& rCurrWindowArea
);
209 virtual void resize( const ::basegfx::B2IRange
& rect
);
210 virtual ::basegfx::B2IVector
getPageSize();
211 virtual ::canvas::ISurfaceSharedPtr
createSurface( const ::basegfx::B2IVector
& surfaceSize
);
212 virtual void beginPrimitive( PrimitiveType eType
);
213 virtual void endPrimitive();
214 virtual void pushVertex( const ::canvas::Vertex
& vertex
);
215 virtual bool isError();
217 COMReference
<IDirect3DDevice9
> getDevice() { return mpDevice
; }
219 void flushVertexCache();
220 void commitVertexCache();
224 bool create( const ::Window
& rWindow
);
226 bool verifyDevice( const UINT nAdapter
);
227 UINT
getAdapterFromWindow();
229 /** This object represents the DirectX state machine. In order
230 to serialize access to DirectX's global state, a global
233 static ::osl::Mutex maMutex
;
236 COMReference
<IDirect3DDevice9
> mpDevice
;
237 COMReference
<IDirect3D9
> mpDirect3D9
;
238 COMReference
<IDirect3DSwapChain9
> mpSwapChain
;
239 COMReference
<IDirect3DVertexBuffer9
> mpVertexBuffer
;
240 ::canvas::ISurfaceSharedPtr mpTexture
;
241 ::boost::scoped_ptr
<SystemChildWindow
> mpWindow
;
242 ::basegfx::B2IVector maSize
;
243 typedef std::vector
<canvas::Vertex
> vertexCache_t
;
244 vertexCache_t maVertexCache
;
246 int mnBeginSceneCount
;
247 bool mbCanUseDynamicTextures
;
249 PrimitiveType meType
;
250 ::basegfx::B2IVector maPageSize
;
251 D3DPRESENT_PARAMETERS mad3dpp
;
253 inline bool isDisposed() const { return (mhWnd
==NULL
); }
262 std::size_t maNumVertices
;
263 std::size_t maWriteIndex
;
264 std::size_t maReadIndex
;
267 ::osl::Mutex
DXRenderModule::maMutex
;
269 //////////////////////////////////////////////////////////////////////////////////
270 // DXSurface::ImplRenderModuleGuard
271 //////////////////////////////////////////////////////////////////////////////////
273 inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
274 DXRenderModule
& rRenderModule
) :
275 mrRenderModule( rRenderModule
)
277 mrRenderModule
.lock();
280 inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
282 mrRenderModule
.unlock();
285 #ifdef FAKE_MAX_NUMBER_TEXTURES
286 static sal_uInt32 gNumSurfaces
= 0;
289 void fillRect( sal_uInt32
*pDest
,
295 for(sal_uInt32 i
=0; i
<dwWidth
; ++i
)
298 pDest
[((dwHeight
-1)*dwPitch
)+i
]=dwColor
;
301 for(sal_uInt32 j
=0; j
<dwHeight
; ++j
)
304 pDest
[dwWidth
-1]=dwColor
;
309 //////////////////////////////////////////////////////////////////////////////////
310 // DXSurface::DXSurface
311 //////////////////////////////////////////////////////////////////////////////////
313 DXSurface::DXSurface( DXRenderModule
& rRenderModule
,
314 const ::basegfx::B2ISize
& rSize
) :
315 mrRenderModule(rRenderModule
),
319 ImplRenderModuleGuard
aGuard( mrRenderModule
);
321 #ifdef FAKE_MAX_NUMBER_TEXTURES
323 if(gNumSurfaces
>= FAKE_MAX_NUMBER_TEXTURES
)
327 #ifdef FAKE_MAX_TEXTURE_SIZE
328 if(rSize
.getX() > FAKE_MAX_TEXTURE_SIZE
)
330 if(rSize
.getY() > FAKE_MAX_TEXTURE_SIZE
)
334 ENSURE_ARG_OR_THROW(rSize
.getX() > 0 && rSize
.getY() > 0,
335 "DXSurface::DXSurface(): request for zero-sized surface");
337 COMReference
<IDirect3DDevice9
> pDevice(rRenderModule
.getDevice());
339 IDirect3DTexture9
*pTexture(NULL
);
340 if(FAILED(pDevice
->CreateTexture(
348 mpTexture
=COMReference
<IDirect3DTexture9
>(pTexture
);
352 //////////////////////////////////////////////////////////////////////////////////
353 // DXSurface::~DXSurface
354 //////////////////////////////////////////////////////////////////////////////////
356 DXSurface::~DXSurface()
358 ImplRenderModuleGuard
aGuard( mrRenderModule
);
360 #ifdef FAKE_MAX_NUMBER_TEXTURES
365 //////////////////////////////////////////////////////////////////////////////////
366 // DXSurface::selectTexture
367 //////////////////////////////////////////////////////////////////////////////////
369 bool DXSurface::selectTexture()
371 ImplRenderModuleGuard
aGuard( mrRenderModule
);
372 mrRenderModule
.flushVertexCache();
373 COMReference
<IDirect3DDevice9
> pDevice(mrRenderModule
.getDevice());
375 if( FAILED(pDevice
->SetTexture(0,mpTexture
.get())) )
381 //////////////////////////////////////////////////////////////////////////////////
382 // DXSurface::isValid
383 //////////////////////////////////////////////////////////////////////////////////
385 bool DXSurface::isValid()
387 ImplRenderModuleGuard
aGuard( mrRenderModule
);
389 if(!(mpTexture
.is()))
394 //////////////////////////////////////////////////////////////////////////////////
396 //////////////////////////////////////////////////////////////////////////////////
398 bool DXSurface::update( const ::basegfx::B2IPoint
& rDestPos
,
399 const ::basegfx::B2IRange
& rSourceRect
,
400 ::canvas::IColorBuffer
& rSource
)
402 ImplRenderModuleGuard
aGuard( mrRenderModule
);
404 // can't update if surface is not valid, that means
405 // either not existent nor restored...
409 D3DLOCKED_RECT aLockedRect
;
411 rect
.left
= std::max(sal_Int32(0),rDestPos
.getX());
412 rect
.top
= std::max(sal_Int32(0),rDestPos
.getY());
413 // to avoid interpolation artifacts from other textures,
414 // the surface manager allocates one pixel gap between
415 // them. Clear that to transparent.
416 rect
.right
= std::min(maSize
.getX(),
417 rect
.left
+ sal_Int32(rSourceRect
.getWidth()+1));
418 rect
.bottom
= std::min(maSize
.getY(),
419 rect
.top
+ sal_Int32(rSourceRect
.getHeight()+1));
420 const bool bClearRightColumn( rect
.right
< maSize
.getX() );
421 const bool bClearBottomRow( rect
.bottom
< maSize
.getY() );
423 if(SUCCEEDED(mpTexture
->LockRect(0,&aLockedRect
,&rect
,D3DLOCK_NOSYSLOCK
)))
425 if(sal_uInt8
* pImage
= rSource
.lock())
427 switch( rSource
.getFormat() )
429 case ::canvas::IColorBuffer::FMT_A8R8G8B8
:
431 const std::size_t nSourceBytesPerPixel(4);
432 const std::size_t nSourcePitchInBytes(rSource
.getStride());
433 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
434 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
436 // calculate the destination memory address
437 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
439 const sal_uInt32
nNumBytesToCopy(
440 static_cast<sal_uInt32
>(
441 rSourceRect
.getWidth())*
442 nSourceBytesPerPixel
);
443 const sal_uInt64
nNumLines(rSourceRect
.getHeight());
445 for(sal_uInt32 i
=0; i
<nNumLines
; ++i
)
447 rtl_copyMemory(pDst
,pImage
,nNumBytesToCopy
);
449 if( bClearRightColumn
)
451 // to avoid interpolation artifacts
452 // from other textures, the surface
453 // manager allocates one pixel gap
454 // between them. Clear that to
456 pDst
[nNumBytesToCopy
] =
457 pDst
[nNumBytesToCopy
+1] =
458 pDst
[nNumBytesToCopy
+2] =
459 pDst
[nNumBytesToCopy
+3] = 0x00;
461 pDst
+= aLockedRect
.Pitch
;
462 pImage
+= nSourcePitchInBytes
;
465 if( bClearBottomRow
)
466 rtl_zeroMemory(pDst
,nNumBytesToCopy
+4);
470 case ::canvas::IColorBuffer::FMT_R8G8B8
:
472 const std::size_t nSourceBytesPerPixel(3);
473 const std::size_t nSourcePitchInBytes(rSource
.getStride());
474 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
475 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
477 // calculate the destination memory address
478 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
480 const sal_Int32
nNumColumns(
481 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getWidth()));
482 const sal_Int32
nNumLines(
483 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getHeight()));
484 for(sal_Int32 i
=0; i
<nNumLines
; ++i
)
486 sal_uInt32
*pDstScanline
= reinterpret_cast<sal_uInt32
*>(pDst
);
487 sal_uInt8
*pSrcScanline
= reinterpret_cast<sal_uInt8
*>(pImage
);
489 for(sal_Int32 x
=0; x
<nNumColumns
; ++x
)
491 sal_uInt32
color(0xFF000000);
492 color
|= pSrcScanline
[2]<<16;
493 color
|= pSrcScanline
[1]<<8;
494 color
|= pSrcScanline
[0];
496 *pDstScanline
++ = color
;
498 if( bClearRightColumn
)
499 *pDstScanline
++ = 0xFF000000;
501 pDst
+= aLockedRect
.Pitch
;
502 pImage
+= nSourcePitchInBytes
;
505 if( bClearBottomRow
)
506 rtl_zeroMemory(pDst
,4*(nNumColumns
+1));
510 case ::canvas::IColorBuffer::FMT_X8R8G8B8
:
512 const std::size_t nSourceBytesPerPixel(4);
513 const std::size_t nSourcePitchInBytes(rSource
.getStride());
514 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
515 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
517 // calculate the destination memory address
518 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
520 const sal_Int32
nNumLines(
521 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getHeight()));
522 const sal_Int32
nNumColumns(
523 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getWidth()));
524 for(sal_Int32 i
=0; i
<nNumLines
; ++i
)
526 sal_uInt32
*pSrc32
= reinterpret_cast<sal_uInt32
*>(pImage
);
527 sal_uInt32
*pDst32
= reinterpret_cast<sal_uInt32
*>(pDst
);
528 for(sal_Int32 j
=0; j
<nNumColumns
; ++j
)
529 pDst32
[j
] = 0xFF000000 | pSrc32
[j
];
531 if( bClearRightColumn
)
532 pDst32
[nNumColumns
] = 0xFF000000;
534 pDst
+= aLockedRect
.Pitch
;
535 pImage
+= nSourcePitchInBytes
;
538 if( bClearBottomRow
)
539 rtl_zeroMemory(pDst
,4*(nNumColumns
+1));
544 ENSURE_OR_RETURN_FALSE(false,
545 "DXSurface::update(): Unknown/unimplemented buffer format" );
552 return SUCCEEDED(mpTexture
->UnlockRect(0));
558 //////////////////////////////////////////////////////////////////////////////////
559 // DXSurface::getSize
560 //////////////////////////////////////////////////////////////////////////////////
562 ::basegfx::B2IVector
DXSurface::getSize()
567 COMReference
<IDirect3DTexture9
> DXSurface::getTexture() const
572 //////////////////////////////////////////////////////////////////////////////////
573 // DXRenderModule::DXRenderModule
574 //////////////////////////////////////////////////////////////////////////////////
576 DXRenderModule::DXRenderModule( const ::Window
& rWindow
) :
586 mnBeginSceneCount(0),
587 mbCanUseDynamicTextures(false),
589 meType( PRIMITIVE_TYPE_UNKNOWN
),
592 maNumVertices( VERTEX_BUFFER_SIZE
),
596 // TODO(P2): get rid of those fine-grained locking
597 ::osl::MutexGuard
aGuard( maMutex
);
599 if(!(create(rWindow
)))
601 throw lang::NoSupportException(
602 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
603 "Could not create DirectX device!") ),NULL
);
606 // allocate a single texture surface which can be used later.
607 // we also use this to calibrate the page size.
608 ::basegfx::B2IVector
aPageSize(maPageSize
);
611 mpTexture
= ::canvas::ISurfaceSharedPtr(
612 new DXSurface(*this,aPageSize
));
613 if(mpTexture
->isValid())
616 aPageSize
.setX(aPageSize
.getX()>>1);
617 aPageSize
.setY(aPageSize
.getY()>>1);
618 if((aPageSize
.getX() < MIN_TEXTURE_SIZE
) ||
619 (aPageSize
.getY() < MIN_TEXTURE_SIZE
))
621 throw lang::NoSupportException(
622 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
623 "Could not create DirectX device - "
624 "insufficient texture space!") ),NULL
);
627 maPageSize
=aPageSize
;
629 IDirect3DVertexBuffer9
*pVB(NULL
);
630 DWORD
aFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
);
631 if( FAILED(mpDevice
->CreateVertexBuffer(sizeof(dxvertex
)*maNumVertices
,
632 D3DUSAGE_DYNAMIC
|D3DUSAGE_WRITEONLY
,
638 throw lang::NoSupportException(
639 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
640 "Could not create DirectX device - out of memory!")),NULL
);
643 mpVertexBuffer
=COMReference
<IDirect3DVertexBuffer9
>(pVB
);
646 //////////////////////////////////////////////////////////////////////////////////
647 // DXRenderModule::~DXRenderModule
648 //////////////////////////////////////////////////////////////////////////////////
650 DXRenderModule::~DXRenderModule()
655 //////////////////////////////////////////////////////////////////////////////////
656 // DXRenderModule::disposing
657 //////////////////////////////////////////////////////////////////////////////////
659 void DXRenderModule::disposing()
668 // refrain from releasing the DX9 objects. We're the only
669 // ones holding references to them, and it might be
670 // dangerous to destroy the DX9 device, before all other
674 //////////////////////////////////////////////////////////////////////////////////
675 // DXRenderModule::create
676 //////////////////////////////////////////////////////////////////////////////////
678 bool DXRenderModule::create( const ::Window
& rWindow
)
680 // TODO(P2): get rid of those fine-grained locking
681 ::osl::MutexGuard
aGuard( maMutex
);
683 maVertexCache
.reserve(1024);
686 new SystemChildWindow(
687 const_cast<Window
*>(&rWindow
), 0) );
689 // system child window must not receive mouse events
690 mpWindow
->SetMouseTransparent( TRUE
);
692 // parent should receive paint messages as well
693 // [PARENTCLIPMODE_NOCLIP], the argument is here
694 // passed as plain numeric value since the stupid
695 // define utilizes a USHORT cast.
696 mpWindow
->SetParentClipMode(0x0002);
698 // the system child window must not clear its background
699 mpWindow
->EnableEraseBackground( sal_False
);
701 mpWindow
->SetControlForeground();
702 mpWindow
->SetControlBackground();
703 mpWindow
->EnablePaint(sal_False
);
705 const SystemEnvData
*pData
= mpWindow
->GetSystemData();
706 const HWND
hwnd(reinterpret_cast<HWND
>(pData
->hWnd
));
707 mhWnd
= const_cast<HWND
>(hwnd
);
709 ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND
>(mhWnd
) ),
710 "DXRenderModule::create() No valid HWND given." );
712 // retrieve position and size of the parent window
713 const ::Size
&rSizePixel(rWindow
.GetSizePixel());
715 // remember the size of the parent window, since we
716 // need to use this for our child window.
717 maSize
.setX(static_cast<sal_Int32
>(rSizePixel
.Width()));
718 maSize
.setY(static_cast<sal_Int32
>(rSizePixel
.Height()));
720 // let the child window cover the same size as the parent window.
721 mpWindow
->SetPosSizePixel(0,0,maSize
.getX(),maSize
.getY());
723 // TODO(F2): since we would like to share precious hardware
724 // resources, the direct3d9 object should be global. each new
725 // request for a canvas should only create a new swapchain.
726 mpDirect3D9
= COMReference
<IDirect3D9
>(
727 Direct3DCreate9(D3D_SDK_VERSION
));
728 if(!mpDirect3D9
.is())
731 // create a device from the direct3d9 object.
732 if(!(createDevice()))
740 //////////////////////////////////////////////////////////////////////////////////
741 // DXRenderModule::verifyDevice
742 //////////////////////////////////////////////////////////////////////////////////
744 bool DXRenderModule::verifyDevice( const UINT nAdapter
)
746 ENSURE_OR_THROW( mpDirect3D9
.is(),
747 "DXRenderModule::verifyDevice() No valid device." );
749 // ask direct3d9 about the capabilities of hardware devices on a specific adapter.
750 // here we decide if the underlying hardware of the machine 'is good enough'.
751 // since we only need a tiny little fraction of what could be used, this
752 // is basically a no-op.
754 if(FAILED(mpDirect3D9
->GetDeviceCaps(nAdapter
,D3DDEVTYPE_HAL
,&aCaps
)))
756 if(!(aCaps
.MaxTextureWidth
))
758 if(!(aCaps
.MaxTextureHeight
))
760 maPageSize
= ::basegfx::B2IVector(aCaps
.MaxTextureWidth
,aCaps
.MaxTextureHeight
);
762 // check device against white & blacklist entries
763 D3DADAPTER_IDENTIFIER9 aIdent
;
764 if(FAILED(mpDirect3D9
->GetAdapterIdentifier(nAdapter
,0,&aIdent
)))
767 DXCanvasItem aConfigItem
;
768 DXCanvasItem::DeviceInfo aInfo
;
769 aInfo
.nVendorId
= aIdent
.VendorId
;
770 aInfo
.nDeviceId
= aIdent
.DeviceId
;
771 aInfo
.nDeviceSubSysId
= aIdent
.SubSysId
;
772 aInfo
.nDeviceRevision
= aIdent
.Revision
;
774 aInfo
.nDriverId
= HIWORD(aIdent
.DriverVersion
.HighPart
);
775 aInfo
.nDriverVersion
= LOWORD(aIdent
.DriverVersion
.HighPart
);
776 aInfo
.nDriverSubVersion
= HIWORD(aIdent
.DriverVersion
.LowPart
);
777 aInfo
.nDriverBuildId
= LOWORD(aIdent
.DriverVersion
.LowPart
);
779 if( !aConfigItem
.isDeviceUsable(aInfo
) )
782 if( aConfigItem
.isBlacklistCurrentDevice() )
784 aConfigItem
.blacklistDevice(aInfo
);
788 aConfigItem
.adaptMaxTextureSize(maPageSize
);
790 mbCanUseDynamicTextures
= (aCaps
.Caps2
& D3DCAPS2_DYNAMICTEXTURES
) != 0;
796 //////////////////////////////////////////////////////////////////////////////////
797 // DXRenderModule::createDevice
798 //////////////////////////////////////////////////////////////////////////////////
800 bool DXRenderModule::createDevice()
802 // we expect that the caller provides us with a valid HWND
803 ENSURE_OR_THROW( IsWindow(mhWnd
),
804 "DXRenderModule::createDevice() No valid HWND given." );
806 // we expect that the caller already created the direct3d9 object.
807 ENSURE_OR_THROW( mpDirect3D9
.is(),
808 "DXRenderModule::createDevice() no direct3d?." );
810 // find the adapter identifier from the window.
811 const UINT
aAdapter(getAdapterFromWindow());
812 if(aAdapter
== static_cast<UINT
>(-1))
815 // verify that device possibly works
816 if( !verifyDevice(aAdapter
) )
819 // query the display mode from the selected adapter.
820 // we'll later request the backbuffer format to be same
821 // same as the display format.
822 D3DDISPLAYMODE d3ddm
;
823 mpDirect3D9
->GetAdapterDisplayMode(aAdapter
,&d3ddm
);
825 // we need to use D3DSWAPEFFECT_COPY here since the canvas-api has
826 // basically nothing to do with efficient resource handling. it tries
827 // to avoid drawing whenevery possible, which is simply not the most
828 // efficient way we could leverage the hardware in this case. it would
829 // be far better to redraw the backbuffer each time we would like to
830 // display the content of the backbuffer, but we need to face reality
831 // here and follow how the canvas was designed.
833 // Strictly speaking, we don't need a full screen worth of
834 // backbuffer here. We could also scale dynamically with
835 // the current window size, but this will make it
836 // necessary to temporarily have two buffers while copying
837 // from the old to the new one. What's more, at the time
838 // we need a larger buffer, DX might not have sufficient
839 // resources available, and we're then left with too small
840 // a back buffer, and no way of falling back to a
841 // different canvas implementation.
842 ZeroMemory( &mad3dpp
, sizeof(mad3dpp
) );
843 mad3dpp
.BackBufferWidth
= std::max(sal_Int32(maSize
.getX()),
844 sal_Int32(d3ddm
.Width
));
845 mad3dpp
.BackBufferHeight
= std::max(sal_Int32(maSize
.getY()),
846 sal_Int32(d3ddm
.Height
));
847 mad3dpp
.BackBufferCount
= 1;
848 mad3dpp
.Windowed
= TRUE
;
849 mad3dpp
.SwapEffect
= D3DSWAPEFFECT_COPY
;
850 mad3dpp
.BackBufferFormat
= d3ddm
.Format
;
851 mad3dpp
.EnableAutoDepthStencil
= FALSE
;
852 mad3dpp
.hDeviceWindow
= mhWnd
;
853 mad3dpp
.PresentationInterval
= D3DPRESENT_INTERVAL_ONE
;
855 // now create the device, first try hardware vertex processing,
856 // then software vertex processing. if both queries fail, we give up
857 // and indicate failure.
858 IDirect3DDevice9
*pDevice(NULL
);
859 if(FAILED(mpDirect3D9
->CreateDevice(aAdapter
,
862 D3DCREATE_HARDWARE_VERTEXPROCESSING
|
863 D3DCREATE_MULTITHREADED
|D3DCREATE_FPU_PRESERVE
,
866 if(FAILED(mpDirect3D9
->CreateDevice(aAdapter
,
869 D3DCREATE_SOFTWARE_VERTEXPROCESSING
|
870 D3DCREATE_MULTITHREADED
|D3DCREATE_FPU_PRESERVE
,
875 // got it, store it in a safe place...
876 mpDevice
=COMReference
<IDirect3DDevice9
>(pDevice
);
878 // After CreateDevice, the first swap chain already exists, so just get it...
879 IDirect3DSwapChain9
*pSwapChain(NULL
);
880 pDevice
->GetSwapChain(0,&pSwapChain
);
881 mpSwapChain
=COMReference
<IDirect3DSwapChain9
>(pSwapChain
);
882 if( !mpSwapChain
.is() )
885 // clear the render target [which is the backbuffer in this case].
886 // we are forced to do this once, and furthermore right now.
887 // please note that this is only possible since we created the
888 // backbuffer with copy semantics [the content is preserved after
889 // calls to Present()], which is an unnecessarily expensive operation.
890 LPDIRECT3DSURFACE9 pBackBuffer
= NULL
;
891 mpSwapChain
->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO
,&pBackBuffer
);
892 mpDevice
->SetRenderTarget( 0, pBackBuffer
);
893 mpDevice
->Clear(0,NULL
,D3DCLEAR_TARGET
,0,1.0f
,0L);
894 pBackBuffer
->Release();
899 //////////////////////////////////////////////////////////////////////////////////
900 // DXRenderModule::createSystemMemorySurface
901 //////////////////////////////////////////////////////////////////////////////////
903 COMReference
<IDirect3DSurface9
> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector
& rSize
)
906 return COMReference
<IDirect3DSurface9
>(NULL
);
908 // please note that D3DFMT_X8R8G8B8 is the only format we're
909 // able to choose here, since GetDC() doesn't support any
910 // other 32bit-format.
911 IDirect3DSurface9
*pSurface(NULL
);
912 if( FAILED(mpDevice
->CreateOffscreenPlainSurface(
920 throw lang::NoSupportException(
921 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
922 "Could not create offscreen surface - out of mem!") ),NULL
);
925 return COMReference
<IDirect3DSurface9
>(pSurface
);
928 //////////////////////////////////////////////////////////////////////////////////
929 // DXRenderModule::flip
930 //////////////////////////////////////////////////////////////////////////////////
932 bool DXRenderModule::flip( const ::basegfx::B2IRectangle
& rUpdateArea
,
933 const ::basegfx::B2IRectangle
& /*rCurrWindowArea*/ )
935 // TODO(P2): get rid of those fine-grained locking
936 ::osl::MutexGuard
aGuard( maMutex
);
938 if(isDisposed() || !mpSwapChain
.is())
943 // TODO(P2): Might be faster to actually pass update area here
946 rUpdateArea
.getMinX(),
947 rUpdateArea
.getMinY(),
948 rUpdateArea
.getMaxX(),
949 rUpdateArea
.getMaxY()
951 HRESULT
hr(mpSwapChain
->Present(&aRect
,&aRect
,NULL
,NULL
,0));
954 if(hr
!= D3DERR_DEVICELOST
)
957 // interestingly enough, sometimes the Reset() below
958 // *still* causes DeviceLost errors. So, cycle until
959 // DX was kind enough to really reset the device...
962 mpVertexBuffer
.reset();
963 hr
= mpDevice
->Reset(&mad3dpp
);
966 IDirect3DVertexBuffer9
*pVB(NULL
);
967 DWORD
aFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
);
968 if( FAILED(mpDevice
->CreateVertexBuffer(sizeof(dxvertex
)*maNumVertices
,
969 D3DUSAGE_DYNAMIC
|D3DUSAGE_WRITEONLY
,
975 throw lang::NoSupportException(
976 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
977 "Could not create DirectX device - out of memory!")),NULL
);
979 mpVertexBuffer
=COMReference
<IDirect3DVertexBuffer9
>(pVB
);
981 // retry after the restore
982 if(SUCCEEDED(mpSwapChain
->Present(&aRect
,&aRect
,NULL
,NULL
,0)))
989 osl_waitThread(&aTimeout
);
991 while(hr
== D3DERR_DEVICELOST
);
999 //////////////////////////////////////////////////////////////////////////////////
1000 // DXRenderModule::screenShot
1001 //////////////////////////////////////////////////////////////////////////////////
1003 void DXRenderModule::screenShot()
1007 //////////////////////////////////////////////////////////////////////////////////
1008 // DXRenderModule::resize
1009 //////////////////////////////////////////////////////////////////////////////////
1011 void DXRenderModule::resize( const ::basegfx::B2IRange
& rect
)
1013 // TODO(P2): get rid of those fine-grained locking
1014 ::osl::MutexGuard
aGuard( maMutex
);
1019 // don't do anything if the size didn't change.
1020 if(maSize
.getX() == static_cast<sal_Int32
>(rect
.getWidth()) &&
1021 maSize
.getY() == static_cast<sal_Int32
>(rect
.getHeight()))
1024 // TODO(Q2): use numeric cast to prevent overflow
1025 maSize
.setX(static_cast<sal_Int32
>(rect
.getWidth()));
1026 maSize
.setY(static_cast<sal_Int32
>(rect
.getHeight()));
1028 mpWindow
->SetPosSizePixel(0,0,maSize
.getX(),maSize
.getY());
1030 // resize back buffer, if necessary
1031 // -------------------------------------------------------------
1033 // don't attempt to create anything if the
1034 // requested size is NULL.
1035 if(!(maSize
.getX()))
1037 if(!(maSize
.getY()))
1040 // backbuffer too small (might happen, if window is
1041 // maximized across multiple monitors)
1042 if( sal_Int32(mad3dpp
.BackBufferWidth
) < maSize
.getX() ||
1043 sal_Int32(mad3dpp
.BackBufferHeight
) < maSize
.getY() )
1045 mad3dpp
.BackBufferWidth
= maSize
.getX();
1046 mad3dpp
.BackBufferHeight
= maSize
.getY();
1048 // clear before, save resources
1049 mpSwapChain
.reset();
1051 IDirect3DSwapChain9
*pSwapChain(NULL
);
1052 if(FAILED(mpDevice
->CreateAdditionalSwapChain(&mad3dpp
,&pSwapChain
)))
1054 mpSwapChain
=COMReference
<IDirect3DSwapChain9
>(pSwapChain
);
1056 // clear the render target [which is the backbuffer in this case].
1057 // we are forced to do this once, and furthermore right now.
1058 // please note that this is only possible since we created the
1059 // backbuffer with copy semantics [the content is preserved after
1060 // calls to Present()], which is an unnecessarily expensive operation.
1061 LPDIRECT3DSURFACE9 pBackBuffer
= NULL
;
1062 mpSwapChain
->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO
,&pBackBuffer
);
1063 mpDevice
->SetRenderTarget( 0, pBackBuffer
);
1064 mpDevice
->Clear(0,NULL
,D3DCLEAR_TARGET
,0,1.0f
,0L);
1065 pBackBuffer
->Release();
1069 //////////////////////////////////////////////////////////////////////////////////
1070 // DXRenderModule::getPageSize
1071 //////////////////////////////////////////////////////////////////////////////////
1073 ::basegfx::B2IVector
DXRenderModule::getPageSize()
1075 // TODO(P2): get rid of those fine-grained locking
1076 ::osl::MutexGuard
aGuard( maMutex
);
1080 //////////////////////////////////////////////////////////////////////////////////
1081 // DXRenderModule::createSurface
1082 //////////////////////////////////////////////////////////////////////////////////
1084 ::canvas::ISurfaceSharedPtr
DXRenderModule::createSurface( const ::basegfx::B2IVector
& surfaceSize
)
1086 // TODO(P2): get rid of those fine-grained locking
1087 ::osl::MutexGuard
aGuard( maMutex
);
1090 return ::canvas::ISurfaceSharedPtr();
1092 const ::basegfx::B2IVector
& rPageSize( getPageSize() );
1093 ::basegfx::B2ISize
aSize(surfaceSize
);
1095 aSize
.setX(rPageSize
.getX());
1097 aSize
.setY(rPageSize
.getY());
1099 if(mpTexture
.use_count() == 1)
1102 return ::canvas::ISurfaceSharedPtr( new DXSurface(*this,aSize
) );
1105 //////////////////////////////////////////////////////////////////////////////////
1106 // DXRenderModule::beginPrimitive
1107 //////////////////////////////////////////////////////////////////////////////////
1109 void DXRenderModule::beginPrimitive( PrimitiveType eType
)
1111 // TODO(P2): get rid of those fine-grained locking
1112 ::osl::MutexGuard
aGuard( maMutex
);
1117 ENSURE_OR_THROW( !mnBeginSceneCount
,
1118 "DXRenderModule::beginPrimitive(): nested call" );
1120 ++mnBeginSceneCount
;
1125 //////////////////////////////////////////////////////////////////////////////////
1126 // DXRenderModule::endPrimitive
1127 //////////////////////////////////////////////////////////////////////////////////
1129 void DXRenderModule::endPrimitive()
1131 // TODO(P2): get rid of those fine-grained locking
1132 ::osl::MutexGuard
aGuard( maMutex
);
1137 --mnBeginSceneCount
;
1138 meType
=PRIMITIVE_TYPE_UNKNOWN
;
1142 //////////////////////////////////////////////////////////////////////////////////
1143 // DXRenderModule::pushVertex
1144 //////////////////////////////////////////////////////////////////////////////////
1146 void DXRenderModule::pushVertex( const ::canvas::Vertex
& vertex
)
1148 // TODO(P2): get rid of those fine-grained locking
1149 ::osl::MutexGuard
aGuard( maMutex
);
1156 case PRIMITIVE_TYPE_TRIANGLE
:
1158 maVertexCache
.push_back(vertex
);
1164 case PRIMITIVE_TYPE_QUAD
:
1168 const std::size_t size(maVertexCache
.size());
1169 ::canvas::Vertex
v0(maVertexCache
[size
-1]);
1170 ::canvas::Vertex
v2(maVertexCache
[size
-3]);
1171 maVertexCache
.push_back(v0
);
1172 maVertexCache
.push_back(vertex
);
1173 maVertexCache
.push_back(v2
);
1178 maVertexCache
.push_back(vertex
);
1186 "DXRenderModule::pushVertex(): unexpected primitive type");
1191 //////////////////////////////////////////////////////////////////////////////////
1192 // DXRenderModule::isError
1193 //////////////////////////////////////////////////////////////////////////////////
1195 bool DXRenderModule::isError()
1197 // TODO(P2): get rid of those fine-grained locking
1198 ::osl::MutexGuard
aGuard( maMutex
);
1203 //////////////////////////////////////////////////////////////////////////////////
1204 // DXRenderModule::getAdapterFromWindow
1205 //////////////////////////////////////////////////////////////////////////////////
1207 UINT
DXRenderModule::getAdapterFromWindow()
1209 HMONITOR
hMonitor(aMonitorSupport
.MonitorFromWindow(mhWnd
));
1210 UINT
aAdapterCount(mpDirect3D9
->GetAdapterCount());
1211 for(UINT i
=0; i
<aAdapterCount
; ++i
)
1212 if(hMonitor
== mpDirect3D9
->GetAdapterMonitor(i
))
1214 return static_cast<UINT
>(-1);
1217 //////////////////////////////////////////////////////////////////////////////////
1218 // DXRenderModule::commitVertexCache
1219 //////////////////////////////////////////////////////////////////////////////////
1221 void DXRenderModule::commitVertexCache()
1223 if(maReadIndex
!= maWriteIndex
)
1225 const std::size_t nVertexStride
= sizeof(dxvertex
);
1226 const unsigned int nNumVertices
= maWriteIndex
-maReadIndex
;
1227 const unsigned int nNumPrimitives
= nNumVertices
/ 3;
1229 if(FAILED(mpDevice
->SetStreamSource(0,mpVertexBuffer
.get(),0,nVertexStride
)))
1232 if(FAILED(mpDevice
->SetFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
)))
1235 if(FAILED(mpDevice
->BeginScene()))
1238 mbError
|= FAILED(mpDevice
->DrawPrimitive(D3DPT_TRIANGLELIST
,maReadIndex
,nNumPrimitives
));
1239 mbError
|= FAILED(mpDevice
->EndScene());
1241 maReadIndex
+= nNumVertices
;
1245 //////////////////////////////////////////////////////////////////////////////////
1246 // DXRenderModule::flushVertexCache
1247 //////////////////////////////////////////////////////////////////////////////////
1249 void DXRenderModule::flushVertexCache()
1251 if(!(maVertexCache
.size()))
1256 if( FAILED(mpDevice
->SetRenderState(D3DRS_LIGHTING
,FALSE
)))
1259 // enable texture alpha blending
1260 if( FAILED(mpDevice
->SetRenderState(D3DRS_ALPHABLENDENABLE
,TRUE
)))
1263 mpDevice
->SetSamplerState(0,D3DSAMP_MAGFILTER
,D3DTEXF_LINEAR
);
1264 mpDevice
->SetSamplerState(0,D3DSAMP_MINFILTER
,D3DTEXF_LINEAR
);
1265 mpDevice
->SetSamplerState(0,D3DSAMP_ADDRESSU
,D3DTADDRESS_CLAMP
);
1266 mpDevice
->SetSamplerState(0,D3DSAMP_ADDRESSV
,D3DTADDRESS_CLAMP
);
1268 // configure the fixed-function pipeline.
1269 // the only 'feature' we need here is to modulate the alpha-channels
1270 // from the texture and the interpolated diffuse color. the result
1271 // will then be blended with the backbuffer.
1272 // fragment color = texture color * diffuse.alpha.
1273 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAOP
,D3DTOP_MODULATE
);
1274 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAARG1
,D3DTA_TEXTURE
);
1275 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAARG2
,D3DTA_DIFFUSE
);
1277 // normal combination of object...
1278 if( FAILED(mpDevice
->SetRenderState(D3DRS_SRCBLEND
,D3DBLEND_SRCALPHA
)) )
1281 // ..and background color
1282 if( FAILED(mpDevice
->SetRenderState(D3DRS_DESTBLEND
,D3DBLEND_INVSRCALPHA
)) )
1285 // disable backface culling; this enables us to mirror sprites
1286 // by simply reverting the triangles, which, with enabled
1287 // culling, would be invisible otherwise
1288 if( FAILED(mpDevice
->SetRenderState(D3DRS_CULLMODE
,D3DCULL_NONE
)) )
1293 std::size_t nSize(maVertexCache
.size());
1294 const std::size_t nVertexStride
= sizeof(dxvertex
);
1296 const ::basegfx::B2IVector
aPageSize(getPageSize());
1297 const float nHalfPixelSizeX(0.5f
/aPageSize
.getX());
1298 const float nHalfPixelSizeY(0.5f
/aPageSize
.getY());
1299 vertexCache_t::const_iterator
it(maVertexCache
.begin());
1303 DWORD
dwLockFlags(D3DLOCK_NOOVERWRITE
);
1305 // Check to see if there's space for the current set of
1306 // vertices in the buffer.
1307 if( maNumVertices
- maWriteIndex
< nSize
)
1309 commitVertexCache();
1310 dwLockFlags
= D3DLOCK_DISCARD
;
1315 dxvertex
*vertices(NULL
);
1316 const std::size_t nNumVertices(
1317 std::min(maNumVertices
- maWriteIndex
,
1319 if(FAILED(mpVertexBuffer
->Lock(maWriteIndex
*nVertexStride
,
1320 nNumVertices
*nVertexStride
,
1325 std::size_t nIndex(0);
1326 while( nIndex
< nNumVertices
)
1328 dxvertex
&dest
= vertices
[nIndex
++];
1333 const sal_uInt32
alpha(static_cast<sal_uInt32
>(it
->a
*255.0f
));
1334 dest
.diffuse
=D3DCOLOR_ARGB(alpha
,255,255,255);
1335 dest
.u
=static_cast<float>(it
->u
+ nHalfPixelSizeX
);
1336 dest
.v
=static_cast<float>(it
->v
+ nHalfPixelSizeY
);
1340 mpVertexBuffer
->Unlock();
1342 // Advance to the next position in the vertex buffer.
1343 maWriteIndex
+= nNumVertices
;
1344 nSize
-= nNumVertices
;
1346 commitVertexCache();
1349 maVertexCache
.clear();
1353 //////////////////////////////////////////////////////////////////////////////////
1354 // createRenderModule
1355 //////////////////////////////////////////////////////////////////////////////////
1357 IDXRenderModuleSharedPtr
createRenderModule( const ::Window
& rParent
)
1359 return IDXRenderModuleSharedPtr( new DXRenderModule(rParent
) );