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 #define MAX_TEXTURE_SIZE (2048)
22 #define MIN_TEXTURE_SIZE (32)
23 //#define FAKE_MAX_NUMBER_TEXTURES (2)
24 //#define FAKE_MAX_TEXTURE_SIZE (4096)
26 #define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
27 // vertex buffer (must be divisable
28 // by 3, as each triangle primitive
32 #include <osl/thread.h>
35 #include <vcl/syschild.hxx>
36 #include <vcl/window.hxx>
38 #include <canvas/debug.hxx>
39 #include <canvas/verbosetrace.hxx>
40 #include <tools/diagnose_ex.h>
42 #include <canvas/elapsedtime.hxx>
43 #include <canvas/canvastools.hxx>
44 #include <canvas/rendering/icolorbuffer.hxx>
45 #include <canvas/rendering/isurface.hxx>
46 #include <canvas/rendering/irendermodule.hxx>
47 #include <basegfx/numeric/ftools.hxx>
48 #include <basegfx/vector/b2dsize.hxx>
49 #include <basegfx/vector/b2isize.hxx>
50 #include <basegfx/point/b2ipoint.hxx>
51 #include <basegfx/range/b2irectangle.hxx>
52 #include <boost/scoped_ptr.hpp>
53 #include <com/sun/star/lang/NoSupportException.hpp>
55 #include "dx_rendermodule.hxx"
56 #include "dx_config.hxx"
61 #include "dx_impltools.hxx"
62 #include <vcl/sysdata.hxx>
64 #if defined(DX_DEBUG_IMAGES)
65 # if OSL_DEBUG_LEVEL > 0
72 using namespace ::com::sun::star
;
75 // 'dxcanvas' namespace
91 mhLibrary(LoadLibrary("user32.dll")),
92 mpMonitorFromWindow(NULL
)
95 mpMonitorFromWindow
= reinterpret_cast<fMonitorFromWindow
>(
97 mhLibrary
,"MonitorFromWindow"));
103 FreeLibrary(mhLibrary
);
107 HMONITOR
MonitorFromWindow( HWND hwnd
)
109 // return adapter_default in case something went wrong...
110 if(!(mpMonitorFromWindow
))
112 // MONITOR_DEFAULTTONEAREST
113 const DWORD
dwFlags(0x00000002);
114 return mpMonitorFromWindow(hwnd
,dwFlags
);
119 typedef HMONITOR (WINAPI
*fMonitorFromWindow
)( HWND hwnd
, DWORD dwFlags
);
120 fMonitorFromWindow mpMonitorFromWindow
;
123 monitorSupport aMonitorSupport
;
126 class DXRenderModule
;
132 /** ISurface implementation.
134 @attention holds the DXRenderModule via non-refcounted
135 reference! This is safe with current state of affairs, since
136 the canvas::PageManager holds surface and render module via
137 shared_ptr (and makes sure all surfaces are deleted before its
138 render module member goes out of scope).
140 class DXSurface
: public canvas::ISurface
143 DXSurface( DXRenderModule
& rRenderModule
,
144 const ::basegfx::B2ISize
& rSize
);
147 virtual bool selectTexture();
148 virtual bool isValid();
149 virtual bool update( const ::basegfx::B2IPoint
& rDestPos
,
150 const ::basegfx::B2IRange
& rSourceRect
,
151 ::canvas::IColorBuffer
& rSource
);
152 virtual ::basegfx::B2IVector
getSize();
153 COMReference
<IDirect3DTexture9
> getTexture() const;
156 /// Guard local methods against concurrent access to RenderModule
157 class ImplRenderModuleGuard
: private ::boost::noncopyable
160 explicit inline ImplRenderModuleGuard( DXRenderModule
& rRenderModule
);
161 inline ~ImplRenderModuleGuard();
164 DXRenderModule
& mrRenderModule
;
167 DXRenderModule
& mrRenderModule
;
168 COMReference
<IDirect3DTexture9
> mpTexture
;
170 ::basegfx::B2IVector maSize
;
178 /// Default implementation of IDXRenderModule
179 class DXRenderModule
: public IDXRenderModule
182 explicit DXRenderModule( const vcl::Window
& rWindow
);
185 virtual void lock() const { maMutex
.acquire(); }
186 virtual void unlock() const { maMutex
.release(); }
188 virtual COMReference
<IDirect3DSurface9
>
189 createSystemMemorySurface( const ::basegfx::B2IVector
& rSize
);
190 virtual void disposing();
191 virtual HWND
getHWND() const { return mhWnd
; }
192 virtual void screenShot();
194 virtual bool flip( const ::basegfx::B2IRectangle
& rUpdateArea
,
195 const ::basegfx::B2IRectangle
& rCurrWindowArea
);
197 virtual void resize( const ::basegfx::B2IRange
& rect
);
198 virtual ::basegfx::B2IVector
getPageSize();
199 virtual ::canvas::ISurfaceSharedPtr
createSurface( const ::basegfx::B2IVector
& surfaceSize
);
200 virtual void beginPrimitive( PrimitiveType eType
);
201 virtual void endPrimitive();
202 virtual void pushVertex( const ::canvas::Vertex
& vertex
);
203 virtual bool isError();
205 COMReference
<IDirect3DDevice9
> getDevice() { return mpDevice
; }
207 void flushVertexCache();
208 void commitVertexCache();
212 bool create( const vcl::Window
& rWindow
);
214 bool verifyDevice( const UINT nAdapter
);
215 UINT
getAdapterFromWindow();
217 /** This object represents the DirectX state machine. In order
218 to serialize access to DirectX's global state, a global
221 static ::osl::Mutex maMutex
;
224 COMReference
<IDirect3DDevice9
> mpDevice
;
225 COMReference
<IDirect3D9
> mpDirect3D9
;
226 COMReference
<IDirect3DSwapChain9
> mpSwapChain
;
227 COMReference
<IDirect3DVertexBuffer9
> mpVertexBuffer
;
228 ::canvas::ISurfaceSharedPtr mpTexture
;
229 VclPtr
<SystemChildWindow
> mpWindow
;
230 ::basegfx::B2IVector maSize
;
231 typedef std::vector
<canvas::Vertex
> vertexCache_t
;
232 vertexCache_t maVertexCache
;
234 int mnBeginSceneCount
;
235 bool mbCanUseDynamicTextures
;
237 PrimitiveType meType
;
238 ::basegfx::B2IVector maPageSize
;
239 D3DPRESENT_PARAMETERS mad3dpp
;
241 inline bool isDisposed() const { return (mhWnd
==NULL
); }
250 std::size_t maNumVertices
;
251 std::size_t maWriteIndex
;
252 std::size_t maReadIndex
;
255 ::osl::Mutex
DXRenderModule::maMutex
;
258 // DXSurface::ImplRenderModuleGuard
261 inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
262 DXRenderModule
& rRenderModule
) :
263 mrRenderModule( rRenderModule
)
265 mrRenderModule
.lock();
268 inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
270 mrRenderModule
.unlock();
273 #ifdef FAKE_MAX_NUMBER_TEXTURES
274 static sal_uInt32 gNumSurfaces
= 0;
277 void fillRect( sal_uInt32
*pDest
,
283 for(sal_uInt32 i
=0; i
<dwWidth
; ++i
)
286 pDest
[((dwHeight
-1)*dwPitch
)+i
]=dwColor
;
289 for(sal_uInt32 j
=0; j
<dwHeight
; ++j
)
292 pDest
[dwWidth
-1]=dwColor
;
298 // DXSurface::DXSurface
301 DXSurface::DXSurface( DXRenderModule
& rRenderModule
,
302 const ::basegfx::B2ISize
& rSize
) :
303 mrRenderModule(rRenderModule
),
307 ImplRenderModuleGuard
aGuard( mrRenderModule
);
309 #ifdef FAKE_MAX_NUMBER_TEXTURES
311 if(gNumSurfaces
>= FAKE_MAX_NUMBER_TEXTURES
)
315 #ifdef FAKE_MAX_TEXTURE_SIZE
316 if(rSize
.getX() > FAKE_MAX_TEXTURE_SIZE
)
318 if(rSize
.getY() > FAKE_MAX_TEXTURE_SIZE
)
322 ENSURE_ARG_OR_THROW(rSize
.getX() > 0 && rSize
.getY() > 0,
323 "DXSurface::DXSurface(): request for zero-sized surface");
325 COMReference
<IDirect3DDevice9
> pDevice(rRenderModule
.getDevice());
327 IDirect3DTexture9
*pTexture(NULL
);
328 if(FAILED(pDevice
->CreateTexture(
336 mpTexture
=COMReference
<IDirect3DTexture9
>(pTexture
);
341 // DXSurface::~DXSurface
344 DXSurface::~DXSurface()
346 ImplRenderModuleGuard
aGuard( mrRenderModule
);
348 #ifdef FAKE_MAX_NUMBER_TEXTURES
354 // DXSurface::selectTexture
357 bool DXSurface::selectTexture()
359 ImplRenderModuleGuard
aGuard( mrRenderModule
);
360 mrRenderModule
.flushVertexCache();
361 COMReference
<IDirect3DDevice9
> pDevice(mrRenderModule
.getDevice());
363 if( FAILED(pDevice
->SetTexture(0,mpTexture
.get())) )
370 // DXSurface::isValid
373 bool DXSurface::isValid()
375 ImplRenderModuleGuard
aGuard( mrRenderModule
);
377 if(!(mpTexture
.is()))
386 bool DXSurface::update( const ::basegfx::B2IPoint
& rDestPos
,
387 const ::basegfx::B2IRange
& rSourceRect
,
388 ::canvas::IColorBuffer
& rSource
)
390 ImplRenderModuleGuard
aGuard( mrRenderModule
);
392 // can't update if surface is not valid, that means
393 // either not existent nor restored...
397 D3DLOCKED_RECT aLockedRect
;
399 rect
.left
= std::max(sal_Int32(0),rDestPos
.getX());
400 rect
.top
= std::max(sal_Int32(0),rDestPos
.getY());
401 // to avoid interpolation artifacts from other textures,
402 // the surface manager allocates one pixel gap between
403 // them. Clear that to transparent.
404 rect
.right
= std::min(maSize
.getX(),
405 rect
.left
+ sal_Int32(rSourceRect
.getWidth()+1));
406 rect
.bottom
= std::min(maSize
.getY(),
407 rect
.top
+ sal_Int32(rSourceRect
.getHeight()+1));
408 const bool bClearRightColumn( rect
.right
< maSize
.getX() );
409 const bool bClearBottomRow( rect
.bottom
< maSize
.getY() );
411 if(SUCCEEDED(mpTexture
->LockRect(0,&aLockedRect
,&rect
,D3DLOCK_NOSYSLOCK
)))
413 if(sal_uInt8
* pImage
= rSource
.lock())
415 switch( rSource
.getFormat() )
417 case ::canvas::IColorBuffer::FMT_A8R8G8B8
:
419 const std::size_t nSourceBytesPerPixel(4);
420 const std::size_t nSourcePitchInBytes(rSource
.getStride());
421 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
422 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
424 // calculate the destination memory address
425 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
427 const sal_uInt32
nNumBytesToCopy(
428 static_cast<sal_uInt32
>(
429 rSourceRect
.getWidth())*
430 nSourceBytesPerPixel
);
431 const sal_uInt64
nNumLines(rSourceRect
.getHeight());
433 for(sal_uInt32 i
=0; i
<nNumLines
; ++i
)
435 memcpy(pDst
,pImage
,nNumBytesToCopy
);
437 if( bClearRightColumn
)
439 // to avoid interpolation artifacts
440 // from other textures, the surface
441 // manager allocates one pixel gap
442 // between them. Clear that to
444 pDst
[nNumBytesToCopy
] =
445 pDst
[nNumBytesToCopy
+1] =
446 pDst
[nNumBytesToCopy
+2] =
447 pDst
[nNumBytesToCopy
+3] = 0x00;
449 pDst
+= aLockedRect
.Pitch
;
450 pImage
+= nSourcePitchInBytes
;
453 if( bClearBottomRow
)
454 memset(pDst
, 0, nNumBytesToCopy
+4);
458 case ::canvas::IColorBuffer::FMT_R8G8B8
:
460 const std::size_t nSourceBytesPerPixel(3);
461 const std::size_t nSourcePitchInBytes(rSource
.getStride());
462 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
463 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
465 // calculate the destination memory address
466 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
468 const sal_Int32
nNumColumns(
469 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getWidth()));
470 const sal_Int32
nNumLines(
471 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getHeight()));
472 for(sal_Int32 i
=0; i
<nNumLines
; ++i
)
474 sal_uInt32
*pDstScanline
= reinterpret_cast<sal_uInt32
*>(pDst
);
475 sal_uInt8
*pSrcScanline
= reinterpret_cast<sal_uInt8
*>(pImage
);
477 for(sal_Int32 x
=0; x
<nNumColumns
; ++x
)
479 sal_uInt32
color(0xFF000000);
480 color
|= pSrcScanline
[2]<<16;
481 color
|= pSrcScanline
[1]<<8;
482 color
|= pSrcScanline
[0];
484 *pDstScanline
++ = color
;
486 if( bClearRightColumn
)
487 *pDstScanline
++ = 0xFF000000;
489 pDst
+= aLockedRect
.Pitch
;
490 pImage
+= nSourcePitchInBytes
;
493 if( bClearBottomRow
)
494 memset(pDst
, 0, 4*(nNumColumns
+1));
498 case ::canvas::IColorBuffer::FMT_X8R8G8B8
:
500 const std::size_t nSourceBytesPerPixel(4);
501 const std::size_t nSourcePitchInBytes(rSource
.getStride());
502 pImage
+= rSourceRect
.getMinY()*nSourcePitchInBytes
;
503 pImage
+= rSourceRect
.getMinX()*nSourceBytesPerPixel
;
505 // calculate the destination memory address
506 sal_uInt8
*pDst
= (sal_uInt8
*)aLockedRect
.pBits
;
508 const sal_Int32
nNumLines(
509 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getHeight()));
510 const sal_Int32
nNumColumns(
511 sal::static_int_cast
<sal_Int32
>(rSourceRect
.getWidth()));
512 for(sal_Int32 i
=0; i
<nNumLines
; ++i
)
514 sal_uInt32
*pSrc32
= reinterpret_cast<sal_uInt32
*>(pImage
);
515 sal_uInt32
*pDst32
= reinterpret_cast<sal_uInt32
*>(pDst
);
516 for(sal_Int32 j
=0; j
<nNumColumns
; ++j
)
517 pDst32
[j
] = 0xFF000000 | pSrc32
[j
];
519 if( bClearRightColumn
)
520 pDst32
[nNumColumns
] = 0xFF000000;
522 pDst
+= aLockedRect
.Pitch
;
523 pImage
+= nSourcePitchInBytes
;
526 if( bClearBottomRow
)
527 memset(pDst
, 0, 4*(nNumColumns
+1));
532 ENSURE_OR_RETURN_FALSE(false,
533 "DXSurface::update(): Unknown/unimplemented buffer format" );
540 return SUCCEEDED(mpTexture
->UnlockRect(0));
547 // DXSurface::getSize
550 ::basegfx::B2IVector
DXSurface::getSize()
555 COMReference
<IDirect3DTexture9
> DXSurface::getTexture() const
561 // DXRenderModule::DXRenderModule
564 DXRenderModule::DXRenderModule( const vcl::Window
& rWindow
) :
574 mnBeginSceneCount(0),
575 mbCanUseDynamicTextures(false),
577 meType( PRIMITIVE_TYPE_UNKNOWN
),
580 maNumVertices( VERTEX_BUFFER_SIZE
),
584 // TODO(P2): get rid of those fine-grained locking
585 ::osl::MutexGuard
aGuard( maMutex
);
587 if(!(create(rWindow
)))
589 throw lang::NoSupportException( "Could not create DirectX device!" );
592 // allocate a single texture surface which can be used later.
593 // we also use this to calibrate the page size.
594 ::basegfx::B2IVector
aPageSize(maPageSize
);
597 mpTexture
= ::canvas::ISurfaceSharedPtr(
598 new DXSurface(*this,aPageSize
));
599 if(mpTexture
->isValid())
602 aPageSize
.setX(aPageSize
.getX()>>1);
603 aPageSize
.setY(aPageSize
.getY()>>1);
604 if((aPageSize
.getX() < MIN_TEXTURE_SIZE
) ||
605 (aPageSize
.getY() < MIN_TEXTURE_SIZE
))
607 throw lang::NoSupportException(
608 "Could not create DirectX device - insufficient texture space!" );
611 maPageSize
=aPageSize
;
613 IDirect3DVertexBuffer9
*pVB(NULL
);
614 DWORD
aFVF(D3DFVF_XYZRHW
|D3DFVF_DIFFUSE
|D3DFVF_TEX1
);
615 if( FAILED(mpDevice
->CreateVertexBuffer(sizeof(dxvertex
)*maNumVertices
,
616 D3DUSAGE_DYNAMIC
|D3DUSAGE_WRITEONLY
,
622 throw lang::NoSupportException(
623 "Could not create DirectX device - out of memory!" );
626 mpVertexBuffer
=COMReference
<IDirect3DVertexBuffer9
>(pVB
);
630 // DXRenderModule::~DXRenderModule
633 DXRenderModule::~DXRenderModule()
639 // DXRenderModule::disposing
642 void DXRenderModule::disposing()
648 mpWindow
.disposeAndClear();
651 // refrain from releasing the DX9 objects. We're the only
652 // ones holding references to them, and it might be
653 // dangerous to destroy the DX9 device, before all other
658 // DXRenderModule::create
661 bool DXRenderModule::create( const vcl::Window
& rWindow
)
663 // TODO(P2): get rid of those fine-grained locking
664 ::osl::MutexGuard
aGuard( maMutex
);
666 maVertexCache
.reserve( 1024 );
668 mpWindow
.disposeAndClear();
669 mpWindow
.reset( VclPtr
<SystemChildWindow
>::Create(
670 const_cast<vcl::Window
*>(&rWindow
), 0) );
672 // system child window must not receive mouse events
673 mpWindow
->SetMouseTransparent( TRUE
);
675 // parent should receive paint messages as well
676 // [PARENTCLIPMODE_NOCLIP], the argument is here
677 // passed as plain numeric value since the stupid
678 // define utilizes a USHORT cast.
679 mpWindow
->SetParentClipMode(0x0002);
681 // the system child window must not clear its background
682 mpWindow
->EnableEraseBackground( sal_False
);
684 mpWindow
->SetControlForeground();
685 mpWindow
->SetControlBackground();
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()))
723 // DXRenderModule::verifyDevice
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;
779 // DXRenderModule::createDevice
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();
882 // DXRenderModule::createSystemMemorySurface
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!" );
906 return COMReference
<IDirect3DSurface9
>(pSurface
);
910 // DXRenderModule::flip
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!" );
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
);
980 // DXRenderModule::screenShot
983 void DXRenderModule::screenShot()
988 // DXRenderModule::resize
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
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();
1050 // DXRenderModule::getPageSize
1053 ::basegfx::B2IVector
DXRenderModule::getPageSize()
1055 // TODO(P2): get rid of those fine-grained locking
1056 ::osl::MutexGuard
aGuard( maMutex
);
1061 // DXRenderModule::createSurface
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
) );
1086 // DXRenderModule::beginPrimitive
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
;
1106 // DXRenderModule::endPrimitive
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
;
1123 // DXRenderModule::pushVertex
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");
1171 // DXRenderModule::isError
1174 bool DXRenderModule::isError()
1176 // TODO(P2): get rid of those fine-grained locking
1177 ::osl::MutexGuard
aGuard( maMutex
);
1183 // DXRenderModule::getAdapterFromWindow
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);
1197 // DXRenderModule::commitVertexCache
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
;
1225 // DXRenderModule::flushVertexCache
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();
1333 // createRenderModule
1336 IDXRenderModuleSharedPtr
createRenderModule( const vcl::Window
& rParent
)
1338 return IDXRenderModuleSharedPtr( new DXRenderModule(rParent
) );
1342 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */