1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #if DIRECTX_VERSION == 0x0900
23 #define MAX_TEXTURE_SIZE (2048)
24 #define MIN_TEXTURE_SIZE (32)
25 //#define FAKE_MAX_NUMBER_TEXTURES (2)
26 //#define FAKE_MAX_TEXTURE_SIZE (4096)
28 #define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
29 // vertex buffer (must be divisable
30 // by 3, as each triangle primitive
34 #include <vcl/syschild.hxx>
35 #include <vcl/window.hxx>
37 #include <canvas/debug.hxx>
38 #include <canvas/verbosetrace.hxx>
39 #include <tools/diagnose_ex.h>
41 #include <canvas/elapsedtime.hxx>
42 #include <canvas/canvastools.hxx>
43 #include <canvas/rendering/icolorbuffer.hxx>
44 #include <canvas/rendering/isurface.hxx>
45 #include <canvas/rendering/irendermodule.hxx>
46 #include <basegfx/numeric/ftools.hxx>
47 #include <basegfx/vector/b2dsize.hxx>
48 #include <basegfx/vector/b2isize.hxx>
49 #include <basegfx/point/b2ipoint.hxx>
50 #include <basegfx/range/b2irectangle.hxx>
51 #include <boost/scoped_ptr.hpp>
52 #include <com/sun/star/lang/NoSupportException.hpp>
54 #include "dx_rendermodule.hxx"
55 #include "dx_config.hxx"
60 #include "dx_impltools.hxx"
61 #include <vcl/sysdata.hxx>
63 #if defined(DX_DEBUG_IMAGES)
64 # if OSL_DEBUG_LEVEL > 0
71 using namespace ::com::sun::star
;
73 //////////////////////////////////////////////////////////////////////////////////
74 // 'dxcanvas' namespace
75 //////////////////////////////////////////////////////////////////////////////////
81 //////////////////////////////////////////////////////////////////////////////////
83 //////////////////////////////////////////////////////////////////////////////////
90 mhLibrary(LoadLibrary("user32.dll")),
91 mpMonitorFromWindow(NULL
)
94 mpMonitorFromWindow
= reinterpret_cast<fMonitorFromWindow
>(
96 mhLibrary
,"MonitorFromWindow"));
102 FreeLibrary(mhLibrary
);
106 HMONITOR
MonitorFromWindow( HWND hwnd
)
108 // return adapter_default in case something went wrong...
109 if(!(mpMonitorFromWindow
))
111 // MONITOR_DEFAULTTONEAREST
112 const DWORD
dwFlags(0x00000002);
113 return mpMonitorFromWindow(hwnd
,dwFlags
);
118 typedef HMONITOR (WINAPI
*fMonitorFromWindow
)( HWND hwnd
, DWORD dwFlags
);
119 fMonitorFromWindow mpMonitorFromWindow
;
122 monitorSupport aMonitorSupport
;
125 class DXRenderModule
;
127 //////////////////////////////////////////////////////////////////////////////////
129 //////////////////////////////////////////////////////////////////////////////////
131 /** ISurface implemenation.
133 @attention holds the DXRenderModule via non-refcounted
134 reference! This is safe with current state of affairs, since
135 the canvas::PageManager holds surface and render module via
136 shared_ptr (and makes sure all surfaces are deleted before its
137 render module member goes out of scope).
139 class DXSurface
: public canvas::ISurface
142 DXSurface( DXRenderModule
& rRenderModule
,
143 const ::basegfx::B2ISize
& rSize
);
146 virtual bool selectTexture();
147 virtual bool isValid();
148 virtual bool update( const ::basegfx::B2IPoint
& rDestPos
,
149 const ::basegfx::B2IRange
& rSourceRect
,
150 ::canvas::IColorBuffer
& rSource
);
151 virtual ::basegfx::B2IVector
getSize();
152 COMReference
<IDirect3DTexture9
> getTexture() const;
155 /// Guard local methods against concurrent acces to RenderModule
156 class ImplRenderModuleGuard
: private ::boost::noncopyable
159 explicit inline ImplRenderModuleGuard( DXRenderModule
& rRenderModule
);
160 inline ~ImplRenderModuleGuard();
163 DXRenderModule
& mrRenderModule
;
166 DXRenderModule
& mrRenderModule
;
167 COMReference
<IDirect3DTexture9
> mpTexture
;
169 ::basegfx::B2IVector maSize
;
173 //////////////////////////////////////////////////////////////////////////////////
175 //////////////////////////////////////////////////////////////////////////////////
177 /// Default implementation of IDXRenderModule
178 class DXRenderModule
: public IDXRenderModule
181 explicit DXRenderModule( const ::Window
& rWindow
);
184 virtual void lock() const { maMutex
.acquire(); }
185 virtual void unlock() const { maMutex
.release(); }
187 virtual COMReference
<IDirect3DSurface9
>
188 createSystemMemorySurface( const ::basegfx::B2IVector
& rSize
);
189 virtual void disposing();
190 virtual HWND
getHWND() const { return mhWnd
; }
191 virtual void screenShot();
193 virtual bool flip( const ::basegfx::B2IRectangle
& rUpdateArea
,
194 const ::basegfx::B2IRectangle
& rCurrWindowArea
);
196 virtual void resize( const ::basegfx::B2IRange
& rect
);
197 virtual ::basegfx::B2IVector
getPageSize();
198 virtual ::canvas::ISurfaceSharedPtr
createSurface( const ::basegfx::B2IVector
& surfaceSize
);
199 virtual void beginPrimitive( PrimitiveType eType
);
200 virtual void endPrimitive();
201 virtual void pushVertex( const ::canvas::Vertex
& vertex
);
202 virtual bool isError();
204 COMReference
<IDirect3DDevice9
> getDevice() { return mpDevice
; }
206 void flushVertexCache();
207 void commitVertexCache();
211 bool create( const ::Window
& rWindow
);
213 bool verifyDevice( const UINT nAdapter
);
214 UINT
getAdapterFromWindow();
216 /** This object represents the DirectX state machine. In order
217 to serialize access to DirectX's global state, a global
220 static ::osl::Mutex maMutex
;
223 COMReference
<IDirect3DDevice9
> mpDevice
;
224 COMReference
<IDirect3D9
> mpDirect3D9
;
225 COMReference
<IDirect3DSwapChain9
> mpSwapChain
;
226 COMReference
<IDirect3DVertexBuffer9
> mpVertexBuffer
;
227 ::canvas::ISurfaceSharedPtr mpTexture
;
228 ::boost::scoped_ptr
<SystemChildWindow
> mpWindow
;
229 ::basegfx::B2IVector maSize
;
230 typedef std::vector
<canvas::Vertex
> vertexCache_t
;
231 vertexCache_t maVertexCache
;
233 int mnBeginSceneCount
;
234 bool mbCanUseDynamicTextures
;
236 PrimitiveType meType
;
237 ::basegfx::B2IVector maPageSize
;
238 D3DPRESENT_PARAMETERS mad3dpp
;
240 inline bool isDisposed() const { return (mhWnd
==NULL
); }
249 std::size_t maNumVertices
;
250 std::size_t maWriteIndex
;
251 std::size_t maReadIndex
;
254 ::osl::Mutex
DXRenderModule::maMutex
;
256 //////////////////////////////////////////////////////////////////////////////////
257 // DXSurface::ImplRenderModuleGuard
258 //////////////////////////////////////////////////////////////////////////////////
260 inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
261 DXRenderModule
& rRenderModule
) :
262 mrRenderModule( rRenderModule
)
264 mrRenderModule
.lock();
267 inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
269 mrRenderModule
.unlock();
272 #ifdef FAKE_MAX_NUMBER_TEXTURES
273 static sal_uInt32 gNumSurfaces
= 0;
276 void fillRect( sal_uInt32
*pDest
,
282 for(sal_uInt32 i
=0; i
<dwWidth
; ++i
)
285 pDest
[((dwHeight
-1)*dwPitch
)+i
]=dwColor
;
288 for(sal_uInt32 j
=0; j
<dwHeight
; ++j
)
291 pDest
[dwWidth
-1]=dwColor
;
296 //////////////////////////////////////////////////////////////////////////////////
297 // DXSurface::DXSurface
298 //////////////////////////////////////////////////////////////////////////////////
300 DXSurface::DXSurface( DXRenderModule
& rRenderModule
,
301 const ::basegfx::B2ISize
& rSize
) :
302 mrRenderModule(rRenderModule
),
306 ImplRenderModuleGuard
aGuard( mrRenderModule
);
308 #ifdef FAKE_MAX_NUMBER_TEXTURES
310 if(gNumSurfaces
>= FAKE_MAX_NUMBER_TEXTURES
)
314 #ifdef FAKE_MAX_TEXTURE_SIZE
315 if(rSize
.getX() > FAKE_MAX_TEXTURE_SIZE
)
317 if(rSize
.getY() > FAKE_MAX_TEXTURE_SIZE
)
321 ENSURE_ARG_OR_THROW(rSize
.getX() > 0 && rSize
.getY() > 0,
322 "DXSurface::DXSurface(): request for zero-sized surface");
324 COMReference
<IDirect3DDevice9
> pDevice(rRenderModule
.getDevice());
326 IDirect3DTexture9
*pTexture(NULL
);
327 if(FAILED(pDevice
->CreateTexture(
335 mpTexture
=COMReference
<IDirect3DTexture9
>(pTexture
);
339 //////////////////////////////////////////////////////////////////////////////////
340 // DXSurface::~DXSurface
341 //////////////////////////////////////////////////////////////////////////////////
343 DXSurface::~DXSurface()
345 ImplRenderModuleGuard
aGuard( mrRenderModule
);
347 #ifdef FAKE_MAX_NUMBER_TEXTURES
352 //////////////////////////////////////////////////////////////////////////////////
353 // DXSurface::selectTexture
354 //////////////////////////////////////////////////////////////////////////////////
356 bool DXSurface::selectTexture()
358 ImplRenderModuleGuard
aGuard( mrRenderModule
);
359 mrRenderModule
.flushVertexCache();
360 COMReference
<IDirect3DDevice9
> pDevice(mrRenderModule
.getDevice());
362 if( FAILED(pDevice
->SetTexture(0,mpTexture
.get())) )
368 //////////////////////////////////////////////////////////////////////////////////
369 // DXSurface::isValid
370 //////////////////////////////////////////////////////////////////////////////////
372 bool DXSurface::isValid()
374 ImplRenderModuleGuard
aGuard( mrRenderModule
);
376 if(!(mpTexture
.is()))
381 //////////////////////////////////////////////////////////////////////////////////
383 //////////////////////////////////////////////////////////////////////////////////
385 bool DXSurface::update( const ::basegfx::B2IPoint
& rDestPos
,
386 const ::basegfx::B2IRange
& rSourceRect
,
387 ::canvas::IColorBuffer
& rSource
)
389 ImplRenderModuleGuard
aGuard( mrRenderModule
);
391 // can't update if surface is not valid, that means
392 // either not existent nor restored...
396 D3DLOCKED_RECT aLockedRect
;
398 rect
.left
= std::max(sal_Int32(0),rDestPos
.getX());
399 rect
.top
= std::max(sal_Int32(0),rDestPos
.getY());
400 // to avoid interpolation artifacts from other textures,
401 // the surface manager allocates one pixel gap between
402 // them. Clear that to transparent.
403 rect
.right
= std::min(maSize
.getX(),
404 rect
.left
+ sal_Int32(rSourceRect
.getWidth()+1));
405 rect
.bottom
= std::min(maSize
.getY(),
406 rect
.top
+ sal_Int32(rSourceRect
.getHeight()+1));
407 const bool bClearRightColumn( rect
.right
< maSize
.getX() );
408 const bool bClearBottomRow( rect
.bottom
< maSize
.getY() );
410 if(SUCCEEDED(mpTexture
->LockRect(0,&aLockedRect
,&rect
,D3DLOCK_NOSYSLOCK
)))
412 if(sal_uInt8
* pImage
= rSource
.lock())
414 switch( rSource
.getFormat() )
416 case ::canvas::IColorBuffer::FMT_A8R8G8B8
:
418 const std::size_t nSourceBytesPerPixel(4);
419 const std::size_t nSourcePitchInBytes(rSource
.getStride());
420 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
421 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
423 // calculate the destination memory address
424 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
426 const sal_uInt32
nNumBytesToCopy(
427 static_cast<sal_uInt32
>(
428 rSourceRect
.getWidth())*
429 nSourceBytesPerPixel
);
430 const sal_uInt64
nNumLines(rSourceRect
.getHeight());
432 for(sal_uInt32 i
=0; i
<nNumLines
; ++i
)
434 memcpy(pDst
,pImage
,nNumBytesToCopy
);
436 if( bClearRightColumn
)
438 // to avoid interpolation artifacts
439 // from other textures, the surface
440 // manager allocates one pixel gap
441 // between them. Clear that to
443 pDst
[nNumBytesToCopy
] =
444 pDst
[nNumBytesToCopy
+1] =
445 pDst
[nNumBytesToCopy
+2] =
446 pDst
[nNumBytesToCopy
+3] = 0x00;
448 pDst
+= aLockedRect
.Pitch
;
449 pImage
+= nSourcePitchInBytes
;
452 if( bClearBottomRow
)
453 memset(pDst
, 0, nNumBytesToCopy
+4);
457 case ::canvas::IColorBuffer::FMT_R8G8B8
:
459 const std::size_t nSourceBytesPerPixel(3);
460 const std::size_t nSourcePitchInBytes(rSource
.getStride());
461 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
462 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
464 // calculate the destination memory address
465 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
467 const sal_Int32
nNumColumns(
468 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getWidth()));
469 const sal_Int32
nNumLines(
470 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getHeight()));
471 for(sal_Int32 i
=0; i
<nNumLines
; ++i
)
473 sal_uInt32
*pDstScanline
= reinterpret_cast<sal_uInt32
*>(pDst
);
474 sal_uInt8
*pSrcScanline
= reinterpret_cast<sal_uInt8
*>(pImage
);
476 for(sal_Int32 x
=0; x
<nNumColumns
; ++x
)
478 sal_uInt32
color(0xFF000000);
479 color
|= pSrcScanline
[2]<<16;
480 color
|= pSrcScanline
[1]<<8;
481 color
|= pSrcScanline
[0];
483 *pDstScanline
++ = color
;
485 if( bClearRightColumn
)
486 *pDstScanline
++ = 0xFF000000;
488 pDst
+= aLockedRect
.Pitch
;
489 pImage
+= nSourcePitchInBytes
;
492 if( bClearBottomRow
)
493 memset(pDst
, 0, 4*(nNumColumns
+1));
497 case ::canvas::IColorBuffer::FMT_X8R8G8B8
:
499 const std::size_t nSourceBytesPerPixel(4);
500 const std::size_t nSourcePitchInBytes(rSource
.getStride());
501 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
502 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
504 // calculate the destination memory address
505 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
507 const sal_Int32
nNumLines(
508 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getHeight()));
509 const sal_Int32
nNumColumns(
510 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getWidth()));
511 for(sal_Int32 i
=0; i
<nNumLines
; ++i
)
513 sal_uInt32
*pSrc32
= reinterpret_cast<sal_uInt32
*>(pImage
);
514 sal_uInt32
*pDst32
= reinterpret_cast<sal_uInt32
*>(pDst
);
515 for(sal_Int32 j
=0; j
<nNumColumns
; ++j
)
516 pDst32
[j
] = 0xFF000000 | pSrc32
[j
];
518 if( bClearRightColumn
)
519 pDst32
[nNumColumns
] = 0xFF000000;
521 pDst
+= aLockedRect
.Pitch
;
522 pImage
+= nSourcePitchInBytes
;
525 if( bClearBottomRow
)
526 memset(pDst
, 0, 4*(nNumColumns
+1));
531 ENSURE_OR_RETURN_FALSE(false,
532 "DXSurface::update(): Unknown/unimplemented buffer format" );
539 return SUCCEEDED(mpTexture
->UnlockRect(0));
545 //////////////////////////////////////////////////////////////////////////////////
546 // DXSurface::getSize
547 //////////////////////////////////////////////////////////////////////////////////
549 ::basegfx::B2IVector
DXSurface::getSize()
554 COMReference
<IDirect3DTexture9
> DXSurface::getTexture() const
559 //////////////////////////////////////////////////////////////////////////////////
560 // DXRenderModule::DXRenderModule
561 //////////////////////////////////////////////////////////////////////////////////
563 DXRenderModule::DXRenderModule( const ::Window
& rWindow
) :
573 mnBeginSceneCount(0),
574 mbCanUseDynamicTextures(false),
576 meType( PRIMITIVE_TYPE_UNKNOWN
),
579 maNumVertices( VERTEX_BUFFER_SIZE
),
583 // TODO(P2): get rid of those fine-grained locking
584 ::osl::MutexGuard
aGuard( maMutex
);
586 if(!(create(rWindow
)))
588 throw lang::NoSupportException( "Could not create DirectX device!" ,NULL
);
591 // allocate a single texture surface which can be used later.
592 // we also use this to calibrate the page size.
593 ::basegfx::B2IVector
aPageSize(maPageSize
);
596 mpTexture
= ::canvas::ISurfaceSharedPtr(
597 new DXSurface(*this,aPageSize
));
598 if(mpTexture
->isValid())
601 aPageSize
.setX(aPageSize
.getX()>>1);
602 aPageSize
.setY(aPageSize
.getY()>>1);
603 if((aPageSize
.getX() < MIN_TEXTURE_SIZE
) ||
604 (aPageSize
.getY() < MIN_TEXTURE_SIZE
))
606 throw lang::NoSupportException(
607 "Could not create DirectX device - insufficient texture space!", NULL
);
610 maPageSize
=aPageSize
;
612 IDirect3DVertexBuffer9
*pVB(NULL
);
613 DWORD
aFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
);
614 if( FAILED(mpDevice
->CreateVertexBuffer(sizeof(dxvertex
)*maNumVertices
,
615 D3DUSAGE_DYNAMIC
|D3DUSAGE_WRITEONLY
,
621 throw lang::NoSupportException(
622 "Could not create DirectX device - out of memory!", NULL
);
625 mpVertexBuffer
=COMReference
<IDirect3DVertexBuffer9
>(pVB
);
628 //////////////////////////////////////////////////////////////////////////////////
629 // DXRenderModule::~DXRenderModule
630 //////////////////////////////////////////////////////////////////////////////////
632 DXRenderModule::~DXRenderModule()
637 //////////////////////////////////////////////////////////////////////////////////
638 // DXRenderModule::disposing
639 //////////////////////////////////////////////////////////////////////////////////
641 void DXRenderModule::disposing()
650 // refrain from releasing the DX9 objects. We're the only
651 // ones holding references to them, and it might be
652 // dangerous to destroy the DX9 device, before all other
656 //////////////////////////////////////////////////////////////////////////////////
657 // DXRenderModule::create
658 //////////////////////////////////////////////////////////////////////////////////
660 bool DXRenderModule::create( const ::Window
& rWindow
)
662 // TODO(P2): get rid of those fine-grained locking
663 ::osl::MutexGuard
aGuard( maMutex
);
665 maVertexCache
.reserve(1024);
668 new SystemChildWindow(
669 const_cast<Window
*>(&rWindow
), 0) );
671 // system child window must not receive mouse events
672 mpWindow
->SetMouseTransparent( TRUE
);
674 // parent should receive paint messages as well
675 // [PARENTCLIPMODE_NOCLIP], the argument is here
676 // passed as plain numeric value since the stupid
677 // define utilizes a USHORT cast.
678 mpWindow
->SetParentClipMode(0x0002);
680 // the system child window must not clear its background
681 mpWindow
->EnableEraseBackground( sal_False
);
683 mpWindow
->SetControlForeground();
684 mpWindow
->SetControlBackground();
685 mpWindow
->EnablePaint(sal_False
);
687 const SystemEnvData
*pData
= mpWindow
->GetSystemData();
688 const HWND
hwnd(reinterpret_cast<HWND
>(pData
->hWnd
));
689 mhWnd
= const_cast<HWND
>(hwnd
);
691 ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND
>(mhWnd
) ),
692 "DXRenderModule::create() No valid HWND given." );
694 // retrieve position and size of the parent window
695 const ::Size
&rSizePixel(rWindow
.GetSizePixel());
697 // remember the size of the parent window, since we
698 // need to use this for our child window.
699 maSize
.setX(static_cast<sal_Int32
>(rSizePixel
.Width()));
700 maSize
.setY(static_cast<sal_Int32
>(rSizePixel
.Height()));
702 // let the child window cover the same size as the parent window.
703 mpWindow
->setPosSizePixel(0,0,maSize
.getX(),maSize
.getY());
705 // TODO(F2): since we would like to share precious hardware
706 // resources, the direct3d9 object should be global. each new
707 // request for a canvas should only create a new swapchain.
708 mpDirect3D9
= COMReference
<IDirect3D9
>(
709 Direct3DCreate9(D3D_SDK_VERSION
));
710 if(!mpDirect3D9
.is())
713 // create a device from the direct3d9 object.
714 if(!(createDevice()))
722 //////////////////////////////////////////////////////////////////////////////////
723 // DXRenderModule::verifyDevice
724 //////////////////////////////////////////////////////////////////////////////////
726 bool DXRenderModule::verifyDevice( const UINT nAdapter
)
728 ENSURE_OR_THROW( mpDirect3D9
.is(),
729 "DXRenderModule::verifyDevice() No valid device." );
731 // ask direct3d9 about the capabilities of hardware devices on a specific adapter.
732 // here we decide if the underlying hardware of the machine 'is good enough'.
733 // since we only need a tiny little fraction of what could be used, this
734 // is basically a no-op.
736 if(FAILED(mpDirect3D9
->GetDeviceCaps(nAdapter
,D3DDEVTYPE_HAL
,&aCaps
)))
738 if(!(aCaps
.MaxTextureWidth
))
740 if(!(aCaps
.MaxTextureHeight
))
742 maPageSize
= ::basegfx::B2IVector(aCaps
.MaxTextureWidth
,aCaps
.MaxTextureHeight
);
744 // check device against white & blacklist entries
745 D3DADAPTER_IDENTIFIER9 aIdent
;
746 if(FAILED(mpDirect3D9
->GetAdapterIdentifier(nAdapter
,0,&aIdent
)))
749 DXCanvasItem aConfigItem
;
750 DXCanvasItem::DeviceInfo aInfo
;
751 aInfo
.nVendorId
= aIdent
.VendorId
;
752 aInfo
.nDeviceId
= aIdent
.DeviceId
;
753 aInfo
.nDeviceSubSysId
= aIdent
.SubSysId
;
754 aInfo
.nDeviceRevision
= aIdent
.Revision
;
756 aInfo
.nDriverId
= HIWORD(aIdent
.DriverVersion
.HighPart
);
757 aInfo
.nDriverVersion
= LOWORD(aIdent
.DriverVersion
.HighPart
);
758 aInfo
.nDriverSubVersion
= HIWORD(aIdent
.DriverVersion
.LowPart
);
759 aInfo
.nDriverBuildId
= LOWORD(aIdent
.DriverVersion
.LowPart
);
761 if( !aConfigItem
.isDeviceUsable(aInfo
) )
764 if( aConfigItem
.isBlacklistCurrentDevice() )
766 aConfigItem
.blacklistDevice(aInfo
);
770 aConfigItem
.adaptMaxTextureSize(maPageSize
);
772 mbCanUseDynamicTextures
= (aCaps
.Caps2
& D3DCAPS2_DYNAMICTEXTURES
) != 0;
778 //////////////////////////////////////////////////////////////////////////////////
779 // DXRenderModule::createDevice
780 //////////////////////////////////////////////////////////////////////////////////
782 bool DXRenderModule::createDevice()
784 // we expect that the caller provides us with a valid HWND
785 ENSURE_OR_THROW( IsWindow(mhWnd
),
786 "DXRenderModule::createDevice() No valid HWND given." );
788 // we expect that the caller already created the direct3d9 object.
789 ENSURE_OR_THROW( mpDirect3D9
.is(),
790 "DXRenderModule::createDevice() no direct3d?." );
792 // find the adapter identifier from the window.
793 const UINT
aAdapter(getAdapterFromWindow());
794 if(aAdapter
== static_cast<UINT
>(-1))
797 // verify that device possibly works
798 if( !verifyDevice(aAdapter
) )
801 // query the display mode from the selected adapter.
802 // we'll later request the backbuffer format to be same
803 // same as the display format.
804 D3DDISPLAYMODE d3ddm
;
805 mpDirect3D9
->GetAdapterDisplayMode(aAdapter
,&d3ddm
);
807 // we need to use D3DSWAPEFFECT_COPY here since the canvas-api has
808 // basically nothing to do with efficient resource handling. it tries
809 // to avoid drawing whenevery possible, which is simply not the most
810 // efficient way we could leverage the hardware in this case. it would
811 // be far better to redraw the backbuffer each time we would like to
812 // display the content of the backbuffer, but we need to face reality
813 // here and follow how the canvas was designed.
815 // Strictly speaking, we don't need a full screen worth of
816 // backbuffer here. We could also scale dynamically with
817 // the current window size, but this will make it
818 // necessary to temporarily have two buffers while copying
819 // from the old to the new one. What's more, at the time
820 // we need a larger buffer, DX might not have sufficient
821 // resources available, and we're then left with too small
822 // a back buffer, and no way of falling back to a
823 // different canvas implementation.
824 ZeroMemory( &mad3dpp
, sizeof(mad3dpp
) );
825 mad3dpp
.BackBufferWidth
= std::max(sal_Int32(maSize
.getX()),
826 sal_Int32(d3ddm
.Width
));
827 mad3dpp
.BackBufferHeight
= std::max(sal_Int32(maSize
.getY()),
828 sal_Int32(d3ddm
.Height
));
829 mad3dpp
.BackBufferCount
= 1;
830 mad3dpp
.Windowed
= TRUE
;
831 mad3dpp
.SwapEffect
= D3DSWAPEFFECT_COPY
;
832 mad3dpp
.BackBufferFormat
= d3ddm
.Format
;
833 mad3dpp
.EnableAutoDepthStencil
= FALSE
;
834 mad3dpp
.hDeviceWindow
= mhWnd
;
835 mad3dpp
.PresentationInterval
= D3DPRESENT_INTERVAL_ONE
;
837 // now create the device, first try hardware vertex processing,
838 // then software vertex processing. if both queries fail, we give up
839 // and indicate failure.
840 IDirect3DDevice9
*pDevice(NULL
);
841 if(FAILED(mpDirect3D9
->CreateDevice(aAdapter
,
844 D3DCREATE_HARDWARE_VERTEXPROCESSING
|
845 D3DCREATE_MULTITHREADED
|D3DCREATE_FPU_PRESERVE
,
848 if(FAILED(mpDirect3D9
->CreateDevice(aAdapter
,
851 D3DCREATE_SOFTWARE_VERTEXPROCESSING
|
852 D3DCREATE_MULTITHREADED
|D3DCREATE_FPU_PRESERVE
,
857 // got it, store it in a safe place...
858 mpDevice
=COMReference
<IDirect3DDevice9
>(pDevice
);
860 // After CreateDevice, the first swap chain already exists, so just get it...
861 IDirect3DSwapChain9
*pSwapChain(NULL
);
862 pDevice
->GetSwapChain(0,&pSwapChain
);
863 mpSwapChain
=COMReference
<IDirect3DSwapChain9
>(pSwapChain
);
864 if( !mpSwapChain
.is() )
867 // clear the render target [which is the backbuffer in this case].
868 // we are forced to do this once, and furthermore right now.
869 // please note that this is only possible since we created the
870 // backbuffer with copy semantics [the content is preserved after
871 // calls to Present()], which is an unnecessarily expensive operation.
872 LPDIRECT3DSURFACE9 pBackBuffer
= NULL
;
873 mpSwapChain
->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO
,&pBackBuffer
);
874 mpDevice
->SetRenderTarget( 0, pBackBuffer
);
875 mpDevice
->Clear(0,NULL
,D3DCLEAR_TARGET
,0,1.0f
,0L);
876 pBackBuffer
->Release();
881 //////////////////////////////////////////////////////////////////////////////////
882 // DXRenderModule::createSystemMemorySurface
883 //////////////////////////////////////////////////////////////////////////////////
885 COMReference
<IDirect3DSurface9
> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector
& rSize
)
888 return COMReference
<IDirect3DSurface9
>(NULL
);
890 // please note that D3DFMT_X8R8G8B8 is the only format we're
891 // able to choose here, since GetDC() doesn't support any
892 // other 32bit-format.
893 IDirect3DSurface9
*pSurface(NULL
);
894 if( FAILED(mpDevice
->CreateOffscreenPlainSurface(
902 throw lang::NoSupportException(
903 "Could not create offscreen surface - out of mem!", NULL
);
906 return COMReference
<IDirect3DSurface9
>(pSurface
);
909 //////////////////////////////////////////////////////////////////////////////////
910 // DXRenderModule::flip
911 //////////////////////////////////////////////////////////////////////////////////
913 bool DXRenderModule::flip( const ::basegfx::B2IRectangle
& rUpdateArea
,
914 const ::basegfx::B2IRectangle
& /*rCurrWindowArea*/ )
916 // TODO(P2): get rid of those fine-grained locking
917 ::osl::MutexGuard
aGuard( maMutex
);
919 if(isDisposed() || !mpSwapChain
.is())
924 // TODO(P2): Might be faster to actually pass update area here
927 rUpdateArea
.getMinX(),
928 rUpdateArea
.getMinY(),
929 rUpdateArea
.getMaxX(),
930 rUpdateArea
.getMaxY()
932 HRESULT
hr(mpSwapChain
->Present(&aRect
,&aRect
,NULL
,NULL
,0));
935 if(hr
!= D3DERR_DEVICELOST
)
938 // interestingly enough, sometimes the Reset() below
939 // *still* causes DeviceLost errors. So, cycle until
940 // DX was kind enough to really reset the device...
943 mpVertexBuffer
.reset();
944 hr
= mpDevice
->Reset(&mad3dpp
);
947 IDirect3DVertexBuffer9
*pVB(NULL
);
948 DWORD
aFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
);
949 if( FAILED(mpDevice
->CreateVertexBuffer(sizeof(dxvertex
)*maNumVertices
,
950 D3DUSAGE_DYNAMIC
|D3DUSAGE_WRITEONLY
,
956 throw lang::NoSupportException(
957 "Could not create DirectX device - out of memory!", NULL
);
959 mpVertexBuffer
=COMReference
<IDirect3DVertexBuffer9
>(pVB
);
961 // retry after the restore
962 if(SUCCEEDED(mpSwapChain
->Present(&aRect
,&aRect
,NULL
,NULL
,0)))
969 osl_waitThread(&aTimeout
);
971 while(hr
== D3DERR_DEVICELOST
);
979 //////////////////////////////////////////////////////////////////////////////////
980 // DXRenderModule::screenShot
981 //////////////////////////////////////////////////////////////////////////////////
983 void DXRenderModule::screenShot()
987 //////////////////////////////////////////////////////////////////////////////////
988 // DXRenderModule::resize
989 //////////////////////////////////////////////////////////////////////////////////
991 void DXRenderModule::resize( const ::basegfx::B2IRange
& rect
)
993 // TODO(P2): get rid of those fine-grained locking
994 ::osl::MutexGuard
aGuard( maMutex
);
999 // don't do anything if the size didn't change.
1000 if(maSize
.getX() == static_cast<sal_Int32
>(rect
.getWidth()) &&
1001 maSize
.getY() == static_cast<sal_Int32
>(rect
.getHeight()))
1004 // TODO(Q2): use numeric cast to prevent overflow
1005 maSize
.setX(static_cast<sal_Int32
>(rect
.getWidth()));
1006 maSize
.setY(static_cast<sal_Int32
>(rect
.getHeight()));
1008 mpWindow
->setPosSizePixel(0,0,maSize
.getX(),maSize
.getY());
1010 // resize back buffer, if necessary
1011 // -------------------------------------------------------------
1013 // don't attempt to create anything if the
1014 // requested size is NULL.
1015 if(!(maSize
.getX()))
1017 if(!(maSize
.getY()))
1020 // backbuffer too small (might happen, if window is
1021 // maximized across multiple monitors)
1022 if( sal_Int32(mad3dpp
.BackBufferWidth
) < maSize
.getX() ||
1023 sal_Int32(mad3dpp
.BackBufferHeight
) < maSize
.getY() )
1025 mad3dpp
.BackBufferWidth
= maSize
.getX();
1026 mad3dpp
.BackBufferHeight
= maSize
.getY();
1028 // clear before, save resources
1029 mpSwapChain
.reset();
1031 IDirect3DSwapChain9
*pSwapChain(NULL
);
1032 if(FAILED(mpDevice
->CreateAdditionalSwapChain(&mad3dpp
,&pSwapChain
)))
1034 mpSwapChain
=COMReference
<IDirect3DSwapChain9
>(pSwapChain
);
1036 // clear the render target [which is the backbuffer in this case].
1037 // we are forced to do this once, and furthermore right now.
1038 // please note that this is only possible since we created the
1039 // backbuffer with copy semantics [the content is preserved after
1040 // calls to Present()], which is an unnecessarily expensive operation.
1041 LPDIRECT3DSURFACE9 pBackBuffer
= NULL
;
1042 mpSwapChain
->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO
,&pBackBuffer
);
1043 mpDevice
->SetRenderTarget( 0, pBackBuffer
);
1044 mpDevice
->Clear(0,NULL
,D3DCLEAR_TARGET
,0,1.0f
,0L);
1045 pBackBuffer
->Release();
1049 //////////////////////////////////////////////////////////////////////////////////
1050 // DXRenderModule::getPageSize
1051 //////////////////////////////////////////////////////////////////////////////////
1053 ::basegfx::B2IVector
DXRenderModule::getPageSize()
1055 // TODO(P2): get rid of those fine-grained locking
1056 ::osl::MutexGuard
aGuard( maMutex
);
1060 //////////////////////////////////////////////////////////////////////////////////
1061 // DXRenderModule::createSurface
1062 //////////////////////////////////////////////////////////////////////////////////
1064 ::canvas::ISurfaceSharedPtr
DXRenderModule::createSurface( const ::basegfx::B2IVector
& surfaceSize
)
1066 // TODO(P2): get rid of those fine-grained locking
1067 ::osl::MutexGuard
aGuard( maMutex
);
1070 return ::canvas::ISurfaceSharedPtr();
1072 const ::basegfx::B2IVector
& rPageSize( getPageSize() );
1073 ::basegfx::B2ISize
aSize(surfaceSize
);
1075 aSize
.setX(rPageSize
.getX());
1077 aSize
.setY(rPageSize
.getY());
1079 if(mpTexture
.use_count() == 1)
1082 return ::canvas::ISurfaceSharedPtr( new DXSurface(*this,aSize
) );
1085 //////////////////////////////////////////////////////////////////////////////////
1086 // DXRenderModule::beginPrimitive
1087 //////////////////////////////////////////////////////////////////////////////////
1089 void DXRenderModule::beginPrimitive( PrimitiveType eType
)
1091 // TODO(P2): get rid of those fine-grained locking
1092 ::osl::MutexGuard
aGuard( maMutex
);
1097 ENSURE_OR_THROW( !mnBeginSceneCount
,
1098 "DXRenderModule::beginPrimitive(): nested call" );
1100 ++mnBeginSceneCount
;
1105 //////////////////////////////////////////////////////////////////////////////////
1106 // DXRenderModule::endPrimitive
1107 //////////////////////////////////////////////////////////////////////////////////
1109 void DXRenderModule::endPrimitive()
1111 // TODO(P2): get rid of those fine-grained locking
1112 ::osl::MutexGuard
aGuard( maMutex
);
1117 --mnBeginSceneCount
;
1118 meType
=PRIMITIVE_TYPE_UNKNOWN
;
1122 //////////////////////////////////////////////////////////////////////////////////
1123 // DXRenderModule::pushVertex
1124 //////////////////////////////////////////////////////////////////////////////////
1126 void DXRenderModule::pushVertex( const ::canvas::Vertex
& vertex
)
1128 // TODO(P2): get rid of those fine-grained locking
1129 ::osl::MutexGuard
aGuard( maMutex
);
1136 case PRIMITIVE_TYPE_TRIANGLE
:
1138 maVertexCache
.push_back(vertex
);
1144 case PRIMITIVE_TYPE_QUAD
:
1148 const std::size_t size(maVertexCache
.size());
1149 ::canvas::Vertex
v0(maVertexCache
[size
-1]);
1150 ::canvas::Vertex
v2(maVertexCache
[size
-3]);
1151 maVertexCache
.push_back(v0
);
1152 maVertexCache
.push_back(vertex
);
1153 maVertexCache
.push_back(v2
);
1158 maVertexCache
.push_back(vertex
);
1165 OSL_FAIL("DXRenderModule::pushVertex(): unexpected primitive type");
1170 //////////////////////////////////////////////////////////////////////////////////
1171 // DXRenderModule::isError
1172 //////////////////////////////////////////////////////////////////////////////////
1174 bool DXRenderModule::isError()
1176 // TODO(P2): get rid of those fine-grained locking
1177 ::osl::MutexGuard
aGuard( maMutex
);
1182 //////////////////////////////////////////////////////////////////////////////////
1183 // DXRenderModule::getAdapterFromWindow
1184 //////////////////////////////////////////////////////////////////////////////////
1186 UINT
DXRenderModule::getAdapterFromWindow()
1188 HMONITOR
hMonitor(aMonitorSupport
.MonitorFromWindow(mhWnd
));
1189 UINT
aAdapterCount(mpDirect3D9
->GetAdapterCount());
1190 for(UINT i
=0; i
<aAdapterCount
; ++i
)
1191 if(hMonitor
== mpDirect3D9
->GetAdapterMonitor(i
))
1193 return static_cast<UINT
>(-1);
1196 //////////////////////////////////////////////////////////////////////////////////
1197 // DXRenderModule::commitVertexCache
1198 //////////////////////////////////////////////////////////////////////////////////
1200 void DXRenderModule::commitVertexCache()
1202 if(maReadIndex
!= maWriteIndex
)
1204 const std::size_t nVertexStride
= sizeof(dxvertex
);
1205 const unsigned int nNumVertices
= maWriteIndex
-maReadIndex
;
1206 const unsigned int nNumPrimitives
= nNumVertices
/ 3;
1208 if(FAILED(mpDevice
->SetStreamSource(0,mpVertexBuffer
.get(),0,nVertexStride
)))
1211 if(FAILED(mpDevice
->SetFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
)))
1214 if(FAILED(mpDevice
->BeginScene()))
1217 mbError
|= FAILED(mpDevice
->DrawPrimitive(D3DPT_TRIANGLELIST
,maReadIndex
,nNumPrimitives
));
1218 mbError
|= FAILED(mpDevice
->EndScene());
1220 maReadIndex
+= nNumVertices
;
1224 //////////////////////////////////////////////////////////////////////////////////
1225 // DXRenderModule::flushVertexCache
1226 //////////////////////////////////////////////////////////////////////////////////
1228 void DXRenderModule::flushVertexCache()
1230 if(!(maVertexCache
.size()))
1235 if( FAILED(mpDevice
->SetRenderState(D3DRS_LIGHTING
,FALSE
)))
1238 // enable texture alpha blending
1239 if( FAILED(mpDevice
->SetRenderState(D3DRS_ALPHABLENDENABLE
,TRUE
)))
1242 mpDevice
->SetSamplerState(0,D3DSAMP_MAGFILTER
,D3DTEXF_LINEAR
);
1243 mpDevice
->SetSamplerState(0,D3DSAMP_MINFILTER
,D3DTEXF_LINEAR
);
1244 mpDevice
->SetSamplerState(0,D3DSAMP_ADDRESSU
,D3DTADDRESS_CLAMP
);
1245 mpDevice
->SetSamplerState(0,D3DSAMP_ADDRESSV
,D3DTADDRESS_CLAMP
);
1247 // configure the fixed-function pipeline.
1248 // the only 'feature' we need here is to modulate the alpha-channels
1249 // from the texture and the interpolated diffuse color. the result
1250 // will then be blended with the backbuffer.
1251 // fragment color = texture color * diffuse.alpha.
1252 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAOP
,D3DTOP_MODULATE
);
1253 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAARG1
,D3DTA_TEXTURE
);
1254 mpDevice
->SetTextureStageState(0,D3DTSS_ALPHAARG2
,D3DTA_DIFFUSE
);
1256 // normal combination of object...
1257 if( FAILED(mpDevice
->SetRenderState(D3DRS_SRCBLEND
,D3DBLEND_SRCALPHA
)) )
1260 // ..and background color
1261 if( FAILED(mpDevice
->SetRenderState(D3DRS_DESTBLEND
,D3DBLEND_INVSRCALPHA
)) )
1264 // disable backface culling; this enables us to mirror sprites
1265 // by simply reverting the triangles, which, with enabled
1266 // culling, would be invisible otherwise
1267 if( FAILED(mpDevice
->SetRenderState(D3DRS_CULLMODE
,D3DCULL_NONE
)) )
1272 std::size_t nSize(maVertexCache
.size());
1273 const std::size_t nVertexStride
= sizeof(dxvertex
);
1275 const ::basegfx::B2IVector
aPageSize(getPageSize());
1276 const float nHalfPixelSizeX(0.5f
/aPageSize
.getX());
1277 const float nHalfPixelSizeY(0.5f
/aPageSize
.getY());
1278 vertexCache_t::const_iterator
it(maVertexCache
.begin());
1282 DWORD
dwLockFlags(D3DLOCK_NOOVERWRITE
);
1284 // Check to see if there's space for the current set of
1285 // vertices in the buffer.
1286 if( maNumVertices
- maWriteIndex
< nSize
)
1288 commitVertexCache();
1289 dwLockFlags
= D3DLOCK_DISCARD
;
1294 dxvertex
*vertices(NULL
);
1295 const std::size_t nNumVertices(
1296 std::min(maNumVertices
- maWriteIndex
,
1298 if(FAILED(mpVertexBuffer
->Lock(maWriteIndex
*nVertexStride
,
1299 nNumVertices
*nVertexStride
,
1304 std::size_t nIndex(0);
1305 while( nIndex
< nNumVertices
)
1307 dxvertex
&dest
= vertices
[nIndex
++];
1312 const sal_uInt32
alpha(static_cast<sal_uInt32
>(it
->a
*255.0f
));
1313 dest
.diffuse
=D3DCOLOR_ARGB(alpha
,255,255,255);
1314 dest
.u
=static_cast<float>(it
->u
+ nHalfPixelSizeX
);
1315 dest
.v
=static_cast<float>(it
->v
+ nHalfPixelSizeY
);
1319 mpVertexBuffer
->Unlock();
1321 // Advance to the next position in the vertex buffer.
1322 maWriteIndex
+= nNumVertices
;
1323 nSize
-= nNumVertices
;
1325 commitVertexCache();
1328 maVertexCache
.clear();
1332 //////////////////////////////////////////////////////////////////////////////////
1333 // createRenderModule
1334 //////////////////////////////////////////////////////////////////////////////////
1336 IDXRenderModuleSharedPtr
createRenderModule( const ::Window
& rParent
)
1338 return IDXRenderModuleSharedPtr( new DXRenderModule(rParent
) );
1344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */