Branch libreoffice-5-0-4
[LibreOffice.git] / canvas / source / directx / dx_9rm.cxx
blob2b756fedad9ff5d0306c3f6280af27bd5edd123e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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
29 // has 3 vertices)
30 #include <string.h>
32 #include <osl/thread.h>
33 #include <osl/time.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"
58 #undef WB_LEFT
59 #undef WB_RIGHT
61 #include "dx_impltools.hxx"
62 #include <vcl/sysdata.hxx>
64 #if defined(DX_DEBUG_IMAGES)
65 # if OSL_DEBUG_LEVEL > 0
66 # include <imdebug.h>
67 # undef min
68 # undef max
69 # endif
70 #endif
72 using namespace ::com::sun::star;
75 // 'dxcanvas' namespace
78 namespace dxcanvas
80 namespace
83 // monitorSupport
86 class monitorSupport
88 public:
90 monitorSupport() :
91 mhLibrary(LoadLibrary("user32.dll")),
92 mpMonitorFromWindow(NULL)
94 if(mhLibrary)
95 mpMonitorFromWindow = reinterpret_cast<fMonitorFromWindow>(
96 GetProcAddress(
97 mhLibrary,"MonitorFromWindow"));
100 ~monitorSupport()
102 if(mhLibrary)
103 FreeLibrary(mhLibrary);
104 mhLibrary=0;
107 HMONITOR MonitorFromWindow( HWND hwnd )
109 // return adapter_default in case something went wrong...
110 if(!(mpMonitorFromWindow))
111 return HMONITOR(0);
112 // MONITOR_DEFAULTTONEAREST
113 const DWORD dwFlags(0x00000002);
114 return mpMonitorFromWindow(hwnd,dwFlags);
116 private:
118 HINSTANCE mhLibrary;
119 typedef HMONITOR (WINAPI *fMonitorFromWindow )( HWND hwnd, DWORD dwFlags );
120 fMonitorFromWindow mpMonitorFromWindow;
123 monitorSupport aMonitorSupport;
126 class DXRenderModule;
129 // DXSurface
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
142 public:
143 DXSurface( DXRenderModule& rRenderModule,
144 const ::basegfx::B2ISize& rSize );
145 ~DXSurface();
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;
155 private:
156 /// Guard local methods against concurrent access to RenderModule
157 class ImplRenderModuleGuard : private ::boost::noncopyable
159 public:
160 explicit inline ImplRenderModuleGuard( DXRenderModule& rRenderModule );
161 inline ~ImplRenderModuleGuard();
163 private:
164 DXRenderModule& mrRenderModule;
167 DXRenderModule& mrRenderModule;
168 COMReference<IDirect3DTexture9> mpTexture;
170 ::basegfx::B2IVector maSize;
175 // DXRenderModule
178 /// Default implementation of IDXRenderModule
179 class DXRenderModule : public IDXRenderModule
181 public:
182 explicit DXRenderModule( const vcl::Window& rWindow );
183 ~DXRenderModule();
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();
210 private:
212 bool create( const vcl::Window& rWindow );
213 bool createDevice();
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
219 mutex is required.
221 static ::osl::Mutex maMutex;
223 HWND mhWnd;
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;
233 std::size_t mnCount;
234 int mnBeginSceneCount;
235 bool mbCanUseDynamicTextures;
236 bool mbError;
237 PrimitiveType meType;
238 ::basegfx::B2IVector maPageSize;
239 D3DPRESENT_PARAMETERS mad3dpp;
241 inline bool isDisposed() const { return (mhWnd==NULL); }
243 struct dxvertex
245 float x,y,z,rhw;
246 DWORD diffuse;
247 float u,v;
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;
275 #endif
277 void fillRect( sal_uInt32 *pDest,
278 sal_uInt32 dwWidth,
279 sal_uInt32 dwHeight,
280 sal_uInt32 dwPitch,
281 sal_uInt32 dwColor )
283 for(sal_uInt32 i=0; i<dwWidth; ++i)
285 pDest[i]=dwColor;
286 pDest[((dwHeight-1)*dwPitch)+i]=dwColor;
289 for(sal_uInt32 j=0; j<dwHeight; ++j)
291 pDest[0]=dwColor;
292 pDest[dwWidth-1]=dwColor;
293 pDest += dwPitch;
298 // DXSurface::DXSurface
301 DXSurface::DXSurface( DXRenderModule& rRenderModule,
302 const ::basegfx::B2ISize& rSize ) :
303 mrRenderModule(rRenderModule),
304 mpTexture(NULL),
305 maSize()
307 ImplRenderModuleGuard aGuard( mrRenderModule );
309 #ifdef FAKE_MAX_NUMBER_TEXTURES
310 ++gNumSurfaces;
311 if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
312 return;
313 #endif
315 #ifdef FAKE_MAX_TEXTURE_SIZE
316 if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE)
317 return;
318 if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE)
319 return;
320 #endif
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(
329 rSize.getX(),
330 rSize.getY(),
331 1,0,D3DFMT_A8R8G8B8,
332 D3DPOOL_MANAGED,
333 &pTexture,NULL)))
334 return;
336 mpTexture=COMReference<IDirect3DTexture9>(pTexture);
337 maSize = rSize;
341 // DXSurface::~DXSurface
344 DXSurface::~DXSurface()
346 ImplRenderModuleGuard aGuard( mrRenderModule );
348 #ifdef FAKE_MAX_NUMBER_TEXTURES
349 gNumSurfaces--;
350 #endif
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())) )
364 return false;
366 return true;
370 // DXSurface::isValid
373 bool DXSurface::isValid()
375 ImplRenderModuleGuard aGuard( mrRenderModule );
377 if(!(mpTexture.is()))
378 return false;
379 return true;
383 // DXSurface::update
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...
394 if(!(isValid()))
395 return false;
397 D3DLOCKED_RECT aLockedRect;
398 RECT rect;
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
443 // transparent.
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);
456 break;
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];
483 pSrcScanline += 3;
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));
496 break;
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));
529 break;
531 default:
532 ENSURE_OR_RETURN_FALSE(false,
533 "DXSurface::update(): Unknown/unimplemented buffer format" );
534 break;
537 rSource.unlock();
540 return SUCCEEDED(mpTexture->UnlockRect(0));
543 return true;
547 // DXSurface::getSize
550 ::basegfx::B2IVector DXSurface::getSize()
552 return maSize;
555 COMReference<IDirect3DTexture9> DXSurface::getTexture() const
557 return mpTexture;
561 // DXRenderModule::DXRenderModule
564 DXRenderModule::DXRenderModule( const vcl::Window& rWindow ) :
565 mhWnd(0),
566 mpDevice(),
567 mpDirect3D9(),
568 mpSwapChain(),
569 mpVertexBuffer(),
570 mpTexture(),
571 maSize(),
572 maVertexCache(),
573 mnCount(0),
574 mnBeginSceneCount(0),
575 mbCanUseDynamicTextures(false),
576 mbError( false ),
577 meType( PRIMITIVE_TYPE_UNKNOWN ),
578 maPageSize(),
579 mad3dpp(),
580 maNumVertices( VERTEX_BUFFER_SIZE ),
581 maWriteIndex(0),
582 maReadIndex(0)
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);
595 while(true)
597 mpTexture = ::canvas::ISurfaceSharedPtr(
598 new DXSurface(*this,aPageSize));
599 if(mpTexture->isValid())
600 break;
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,
617 aFVF,
618 D3DPOOL_DEFAULT,
619 &pVB,
620 NULL)) )
622 throw lang::NoSupportException(
623 "Could not create DirectX device - out of memory!" );
626 mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
630 // DXRenderModule::~DXRenderModule
633 DXRenderModule::~DXRenderModule()
635 disposing();
639 // DXRenderModule::disposing
642 void DXRenderModule::disposing()
644 if(!(mhWnd))
645 return;
647 mpTexture.reset();
648 mpWindow.disposeAndClear();
649 mhWnd=NULL;
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
654 // objects are dead.
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())
711 return false;
713 // create a device from the direct3d9 object.
714 if(!(createDevice()))
715 return false;
717 mpWindow->Show();
719 return true;
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.
735 D3DCAPS9 aCaps;
736 if(FAILED(mpDirect3D9->GetDeviceCaps(nAdapter,D3DDEVTYPE_HAL,&aCaps)))
737 return false;
738 if(!(aCaps.MaxTextureWidth))
739 return false;
740 if(!(aCaps.MaxTextureHeight))
741 return false;
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)))
747 return false;
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) )
762 return false;
764 if( aConfigItem.isBlacklistCurrentDevice() )
766 aConfigItem.blacklistDevice(aInfo);
767 return false;
770 aConfigItem.adaptMaxTextureSize(maPageSize);
772 mbCanUseDynamicTextures = (aCaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0;
774 return true;
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))
795 return false;
797 // verify that device possibly works
798 if( !verifyDevice(aAdapter) )
799 return false;
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,
842 D3DDEVTYPE_HAL,
843 mhWnd,
844 D3DCREATE_HARDWARE_VERTEXPROCESSING|
845 D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
846 &mad3dpp,
847 &pDevice)))
848 if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
849 D3DDEVTYPE_HAL,
850 mhWnd,
851 D3DCREATE_SOFTWARE_VERTEXPROCESSING|
852 D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
853 &mad3dpp,
854 &pDevice)))
855 return false;
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() )
865 return false;
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();
878 return true;
882 // DXRenderModule::createSystemMemorySurface
885 COMReference<IDirect3DSurface9> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
887 if(isDisposed())
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(
895 rSize.getX(),
896 rSize.getY(),
897 D3DFMT_X8R8G8B8,
898 D3DPOOL_SYSTEMMEM,
899 &pSurface,
900 NULL)) )
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())
920 return false;
922 flushVertexCache();
924 // TODO(P2): Might be faster to actually pass update area here
925 RECT aRect =
927 rUpdateArea.getMinX(),
928 rUpdateArea.getMinY(),
929 rUpdateArea.getMaxX(),
930 rUpdateArea.getMaxY()
932 HRESULT hr(mpSwapChain->Present(&aRect,&aRect,NULL,NULL,0));
933 if(FAILED(hr))
935 if(hr != D3DERR_DEVICELOST)
936 return false;
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);
945 if(SUCCEEDED(hr))
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,
951 aFVF,
952 D3DPOOL_DEFAULT,
953 &pVB,
954 NULL)) )
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)))
963 return true;
966 TimeValue aTimeout;
967 aTimeout.Seconds=1;
968 aTimeout.Nanosec=0;
969 osl_waitThread(&aTimeout);
971 while(hr == D3DERR_DEVICELOST);
973 return false;
976 return true;
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 );
996 if(isDisposed())
997 return;
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()))
1002 return;
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()))
1016 return;
1017 if(!(maSize.getY()))
1018 return;
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)))
1033 return;
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 );
1057 return maPageSize;
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 );
1069 if(isDisposed())
1070 return ::canvas::ISurfaceSharedPtr();
1072 const ::basegfx::B2IVector& rPageSize( getPageSize() );
1073 ::basegfx::B2ISize aSize(surfaceSize);
1074 if(!(aSize.getX()))
1075 aSize.setX(rPageSize.getX());
1076 if(!(aSize.getY()))
1077 aSize.setY(rPageSize.getY());
1079 if(mpTexture.use_count() == 1)
1080 return mpTexture;
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 );
1094 if(isDisposed())
1095 return;
1097 ENSURE_OR_THROW( !mnBeginSceneCount,
1098 "DXRenderModule::beginPrimitive(): nested call" );
1100 ++mnBeginSceneCount;
1101 meType=eType;
1102 mnCount=0;
1106 // DXRenderModule::endPrimitive
1109 void DXRenderModule::endPrimitive()
1111 // TODO(P2): get rid of those fine-grained locking
1112 ::osl::MutexGuard aGuard( maMutex );
1114 if(isDisposed())
1115 return;
1117 --mnBeginSceneCount;
1118 meType=PRIMITIVE_TYPE_UNKNOWN;
1119 mnCount=0;
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 );
1131 if(isDisposed())
1132 return;
1134 switch(meType)
1136 case PRIMITIVE_TYPE_TRIANGLE:
1138 maVertexCache.push_back(vertex);
1139 ++mnCount;
1140 mnCount &= 3;
1141 break;
1144 case PRIMITIVE_TYPE_QUAD:
1146 if(mnCount == 3)
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);
1154 mnCount=0;
1156 else
1158 maVertexCache.push_back(vertex);
1159 ++mnCount;
1161 break;
1164 default:
1165 OSL_FAIL("DXRenderModule::pushVertex(): unexpected primitive type");
1166 break;
1171 // DXRenderModule::isError
1174 bool DXRenderModule::isError()
1176 // TODO(P2): get rid of those fine-grained locking
1177 ::osl::MutexGuard aGuard( maMutex );
1179 return mbError;
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))
1192 return 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)))
1209 return;
1211 if(FAILED(mpDevice->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)))
1212 return;
1214 if(FAILED(mpDevice->BeginScene()))
1215 return;
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()))
1231 return;
1233 mbError=true;
1235 if( FAILED(mpDevice->SetRenderState(D3DRS_LIGHTING,FALSE)))
1236 return;
1238 // enable texture alpha blending
1239 if( FAILED(mpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE)))
1240 return;
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)) )
1258 return;
1260 // ..and background color
1261 if( FAILED(mpDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA)) )
1262 return;
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)) )
1268 return;
1270 mbError=false;
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());
1280 while( nSize )
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;
1290 maWriteIndex = 0;
1291 maReadIndex = 0;
1294 dxvertex *vertices(NULL);
1295 const std::size_t nNumVertices(
1296 std::min(maNumVertices - maWriteIndex,
1297 nSize));
1298 if(FAILED(mpVertexBuffer->Lock(maWriteIndex*nVertexStride,
1299 nNumVertices*nVertexStride,
1300 (void **)&vertices,
1301 dwLockFlags)))
1302 return;
1304 std::size_t nIndex(0);
1305 while( nIndex < nNumVertices )
1307 dxvertex &dest = vertices[nIndex++];
1308 dest.x=it->x;
1309 dest.y=it->y;
1310 dest.z=it->z;
1311 dest.rhw=1;
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);
1316 ++it;
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: */