fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / canvas / source / directx / dx_9rm.cxx
blobe62a29f79359f62a17c476a23483a6f5cd877744
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 #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
31 // has 3 vertices)
32 #include <string.h>
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"
57 #undef WB_LEFT
58 #undef WB_RIGHT
60 #include "dx_impltools.hxx"
61 #include <vcl/sysdata.hxx>
63 #if defined(DX_DEBUG_IMAGES)
64 # if OSL_DEBUG_LEVEL > 0
65 # include <imdebug.h>
66 # undef min
67 # undef max
68 # endif
69 #endif
71 using namespace ::com::sun::star;
73 //////////////////////////////////////////////////////////////////////////////////
74 // 'dxcanvas' namespace
75 //////////////////////////////////////////////////////////////////////////////////
77 namespace dxcanvas
79 namespace
81 //////////////////////////////////////////////////////////////////////////////////
82 // monitorSupport
83 //////////////////////////////////////////////////////////////////////////////////
85 class monitorSupport
87 public:
89 monitorSupport() :
90 mhLibrary(LoadLibrary("user32.dll")),
91 mpMonitorFromWindow(NULL)
93 if(mhLibrary)
94 mpMonitorFromWindow = reinterpret_cast<fMonitorFromWindow>(
95 GetProcAddress(
96 mhLibrary,"MonitorFromWindow"));
99 ~monitorSupport()
101 if(mhLibrary)
102 FreeLibrary(mhLibrary);
103 mhLibrary=0;
106 HMONITOR MonitorFromWindow( HWND hwnd )
108 // return adapter_default in case something went wrong...
109 if(!(mpMonitorFromWindow))
110 return HMONITOR(0);
111 // MONITOR_DEFAULTTONEAREST
112 const DWORD dwFlags(0x00000002);
113 return mpMonitorFromWindow(hwnd,dwFlags);
115 private:
117 HINSTANCE mhLibrary;
118 typedef HMONITOR (WINAPI *fMonitorFromWindow )( HWND hwnd, DWORD dwFlags );
119 fMonitorFromWindow mpMonitorFromWindow;
122 monitorSupport aMonitorSupport;
125 class DXRenderModule;
127 //////////////////////////////////////////////////////////////////////////////////
128 // DXSurface
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
141 public:
142 DXSurface( DXRenderModule& rRenderModule,
143 const ::basegfx::B2ISize& rSize );
144 ~DXSurface();
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;
154 private:
155 /// Guard local methods against concurrent acces to RenderModule
156 class ImplRenderModuleGuard : private ::boost::noncopyable
158 public:
159 explicit inline ImplRenderModuleGuard( DXRenderModule& rRenderModule );
160 inline ~ImplRenderModuleGuard();
162 private:
163 DXRenderModule& mrRenderModule;
166 DXRenderModule& mrRenderModule;
167 COMReference<IDirect3DTexture9> mpTexture;
169 ::basegfx::B2IVector maSize;
173 //////////////////////////////////////////////////////////////////////////////////
174 // DXRenderModule
175 //////////////////////////////////////////////////////////////////////////////////
177 /// Default implementation of IDXRenderModule
178 class DXRenderModule : public IDXRenderModule
180 public:
181 explicit DXRenderModule( const ::Window& rWindow );
182 ~DXRenderModule();
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();
209 private:
211 bool create( const ::Window& rWindow );
212 bool createDevice();
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
218 mutex is required.
220 static ::osl::Mutex maMutex;
222 HWND mhWnd;
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;
232 std::size_t mnCount;
233 int mnBeginSceneCount;
234 bool mbCanUseDynamicTextures;
235 bool mbError;
236 PrimitiveType meType;
237 ::basegfx::B2IVector maPageSize;
238 D3DPRESENT_PARAMETERS mad3dpp;
240 inline bool isDisposed() const { return (mhWnd==NULL); }
242 struct dxvertex
244 float x,y,z,rhw;
245 DWORD diffuse;
246 float u,v;
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;
274 #endif
276 void fillRect( sal_uInt32 *pDest,
277 sal_uInt32 dwWidth,
278 sal_uInt32 dwHeight,
279 sal_uInt32 dwPitch,
280 sal_uInt32 dwColor )
282 for(sal_uInt32 i=0; i<dwWidth; ++i)
284 pDest[i]=dwColor;
285 pDest[((dwHeight-1)*dwPitch)+i]=dwColor;
288 for(sal_uInt32 j=0; j<dwHeight; ++j)
290 pDest[0]=dwColor;
291 pDest[dwWidth-1]=dwColor;
292 pDest += dwPitch;
296 //////////////////////////////////////////////////////////////////////////////////
297 // DXSurface::DXSurface
298 //////////////////////////////////////////////////////////////////////////////////
300 DXSurface::DXSurface( DXRenderModule& rRenderModule,
301 const ::basegfx::B2ISize& rSize ) :
302 mrRenderModule(rRenderModule),
303 mpTexture(NULL),
304 maSize()
306 ImplRenderModuleGuard aGuard( mrRenderModule );
308 #ifdef FAKE_MAX_NUMBER_TEXTURES
309 ++gNumSurfaces;
310 if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
311 return;
312 #endif
314 #ifdef FAKE_MAX_TEXTURE_SIZE
315 if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE)
316 return;
317 if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE)
318 return;
319 #endif
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(
328 rSize.getX(),
329 rSize.getY(),
330 1,0,D3DFMT_A8R8G8B8,
331 D3DPOOL_MANAGED,
332 &pTexture,NULL)))
333 return;
335 mpTexture=COMReference<IDirect3DTexture9>(pTexture);
336 maSize = rSize;
339 //////////////////////////////////////////////////////////////////////////////////
340 // DXSurface::~DXSurface
341 //////////////////////////////////////////////////////////////////////////////////
343 DXSurface::~DXSurface()
345 ImplRenderModuleGuard aGuard( mrRenderModule );
347 #ifdef FAKE_MAX_NUMBER_TEXTURES
348 gNumSurfaces--;
349 #endif
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())) )
363 return false;
365 return true;
368 //////////////////////////////////////////////////////////////////////////////////
369 // DXSurface::isValid
370 //////////////////////////////////////////////////////////////////////////////////
372 bool DXSurface::isValid()
374 ImplRenderModuleGuard aGuard( mrRenderModule );
376 if(!(mpTexture.is()))
377 return false;
378 return true;
381 //////////////////////////////////////////////////////////////////////////////////
382 // DXSurface::update
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...
393 if(!(isValid()))
394 return false;
396 D3DLOCKED_RECT aLockedRect;
397 RECT rect;
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
442 // transparent.
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);
455 break;
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];
482 pSrcScanline += 3;
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));
495 break;
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));
528 break;
530 default:
531 ENSURE_OR_RETURN_FALSE(false,
532 "DXSurface::update(): Unknown/unimplemented buffer format" );
533 break;
536 rSource.unlock();
539 return SUCCEEDED(mpTexture->UnlockRect(0));
542 return true;
545 //////////////////////////////////////////////////////////////////////////////////
546 // DXSurface::getSize
547 //////////////////////////////////////////////////////////////////////////////////
549 ::basegfx::B2IVector DXSurface::getSize()
551 return maSize;
554 COMReference<IDirect3DTexture9> DXSurface::getTexture() const
556 return mpTexture;
559 //////////////////////////////////////////////////////////////////////////////////
560 // DXRenderModule::DXRenderModule
561 //////////////////////////////////////////////////////////////////////////////////
563 DXRenderModule::DXRenderModule( const ::Window& rWindow ) :
564 mhWnd(0),
565 mpDevice(),
566 mpDirect3D9(),
567 mpSwapChain(),
568 mpVertexBuffer(),
569 mpTexture(),
570 maSize(),
571 maVertexCache(),
572 mnCount(0),
573 mnBeginSceneCount(0),
574 mbCanUseDynamicTextures(false),
575 mbError( false ),
576 meType( PRIMITIVE_TYPE_UNKNOWN ),
577 maPageSize(),
578 mad3dpp(),
579 maNumVertices( VERTEX_BUFFER_SIZE ),
580 maWriteIndex(0),
581 maReadIndex(0)
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);
594 while(true)
596 mpTexture = ::canvas::ISurfaceSharedPtr(
597 new DXSurface(*this,aPageSize));
598 if(mpTexture->isValid())
599 break;
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,
616 aFVF,
617 D3DPOOL_DEFAULT,
618 &pVB,
619 NULL)) )
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()
634 disposing();
637 //////////////////////////////////////////////////////////////////////////////////
638 // DXRenderModule::disposing
639 //////////////////////////////////////////////////////////////////////////////////
641 void DXRenderModule::disposing()
643 if(!(mhWnd))
644 return;
646 mpTexture.reset();
647 mpWindow.reset();
648 mhWnd=NULL;
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
653 // objects are dead.
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);
667 mpWindow.reset(
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())
711 return false;
713 // create a device from the direct3d9 object.
714 if(!(createDevice()))
715 return false;
717 mpWindow->Show();
719 return true;
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.
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;
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))
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;
881 //////////////////////////////////////////////////////////////////////////////////
882 // DXRenderModule::createSystemMemorySurface
883 //////////////////////////////////////////////////////////////////////////////////
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!", 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())
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!", NULL );
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;
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 );
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
1011 // -------------------------------------------------------------
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();
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 );
1057 return maPageSize;
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 );
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) );
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 );
1094 if(isDisposed())
1095 return;
1097 ENSURE_OR_THROW( !mnBeginSceneCount,
1098 "DXRenderModule::beginPrimitive(): nested call" );
1100 ++mnBeginSceneCount;
1101 meType=eType;
1102 mnCount=0;
1105 //////////////////////////////////////////////////////////////////////////////////
1106 // DXRenderModule::endPrimitive
1107 //////////////////////////////////////////////////////////////////////////////////
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;
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 );
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;
1170 //////////////////////////////////////////////////////////////////////////////////
1171 // DXRenderModule::isError
1172 //////////////////////////////////////////////////////////////////////////////////
1174 bool DXRenderModule::isError()
1176 // TODO(P2): get rid of those fine-grained locking
1177 ::osl::MutexGuard aGuard( maMutex );
1179 return mbError;
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))
1192 return 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)))
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;
1224 //////////////////////////////////////////////////////////////////////////////////
1225 // DXRenderModule::flushVertexCache
1226 //////////////////////////////////////////////////////////////////////////////////
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();
1332 //////////////////////////////////////////////////////////////////////////////////
1333 // createRenderModule
1334 //////////////////////////////////////////////////////////////////////////////////
1336 IDXRenderModuleSharedPtr createRenderModule( const ::Window& rParent )
1338 return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) );
1342 #endif
1344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */