xref: /AOO41X/main/canvas/source/directx/dx_9rm.cxx (revision 79aad27f7f29270c03e208e3d687e8e3850af11d)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_canvas.hxx"
26 
27 #if DIRECTX_VERSION == 0x0900
28 
29 #define MAX_TEXTURE_SIZE (2048)
30 #define MIN_TEXTURE_SIZE (32)
31 //#define FAKE_MAX_NUMBER_TEXTURES (2)
32 //#define FAKE_MAX_TEXTURE_SIZE (4096)
33 
34 #define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
35                                    // vertex buffer (must be divisable
36                                    // by 3, as each triangle primitive
37                                    // has 3 vertices)
38 
39 
40 //////////////////////////////////////////////////////////////////////////////////
41 // includes
42 //////////////////////////////////////////////////////////////////////////////////
43 #include <vcl/syschild.hxx>
44 #include <vcl/window.hxx>
45 
46 #include <canvas/debug.hxx>
47 #include <canvas/verbosetrace.hxx>
48 #include <tools/diagnose_ex.h>
49 
50 #include <canvas/elapsedtime.hxx>
51 #include <canvas/canvastools.hxx>
52 #include <canvas/rendering/icolorbuffer.hxx>
53 #include <canvas/rendering/isurface.hxx>
54 #include <canvas/rendering/irendermodule.hxx>
55 #include <basegfx/numeric/ftools.hxx>
56 #include <basegfx/vector/b2dsize.hxx>
57 #include <basegfx/vector/b2isize.hxx>
58 #include <basegfx/point/b2ipoint.hxx>
59 #include <basegfx/range/b2irectangle.hxx>
60 #include <boost/scoped_ptr.hpp>
61 #include <com/sun/star/lang/NoSupportException.hpp>
62 
63 #include "dx_rendermodule.hxx"
64 #include "dx_config.hxx"
65 
66 #undef WB_LEFT
67 #undef WB_RIGHT
68 
69 #include "dx_impltools.hxx"
70 #include <vcl/sysdata.hxx>
71 
72 #if defined(DX_DEBUG_IMAGES)
73 # if OSL_DEBUG_LEVEL > 0
74 #  include <imdebug.h>
75 #  undef min
76 #  undef max
77 # endif
78 #endif
79 
80 using namespace ::com::sun::star;
81 
82 //////////////////////////////////////////////////////////////////////////////////
83 // 'dxcanvas' namespace
84 //////////////////////////////////////////////////////////////////////////////////
85 
86 namespace dxcanvas
87 {
88     namespace
89     {
90         //////////////////////////////////////////////////////////////////////////////////
91         // monitorSupport
92         //////////////////////////////////////////////////////////////////////////////////
93 
94         class monitorSupport
95         {
96         public:
97 
monitorSupport()98             monitorSupport() :
99                 mhLibrary(LoadLibrary("user32.dll")),
100                 mpMonitorFromWindow(NULL)
101             {
102                 if(mhLibrary)
103                     mpMonitorFromWindow = reinterpret_cast<fMonitorFromWindow>(
104                         GetProcAddress(
105                             mhLibrary,"MonitorFromWindow"));
106             }
107 
~monitorSupport()108             ~monitorSupport()
109             {
110                 if(mhLibrary)
111                     FreeLibrary(mhLibrary);
112                 mhLibrary=0;
113             }
114 
MonitorFromWindow(HWND hwnd)115             HMONITOR MonitorFromWindow( HWND hwnd )
116             {
117                 // return adapter_default in case something went wrong...
118                 if(!(mpMonitorFromWindow))
119                     return HMONITOR(0);
120                 // MONITOR_DEFAULTTONEAREST
121                 const DWORD dwFlags(0x00000002);
122                 return mpMonitorFromWindow(hwnd,dwFlags);
123             }
124         private:
125 
126             HINSTANCE mhLibrary;
127             typedef HMONITOR (WINAPI *fMonitorFromWindow )( HWND hwnd, DWORD dwFlags );
128             fMonitorFromWindow mpMonitorFromWindow;
129         };
130 
131         monitorSupport aMonitorSupport;
132 
133 
134         class DXRenderModule;
135 
136         //////////////////////////////////////////////////////////////////////////////////
137         // DXSurface
138         //////////////////////////////////////////////////////////////////////////////////
139 
140         /** ISurface implemenation.
141 
142             @attention holds the DXRenderModule via non-refcounted
143             reference! This is safe with current state of affairs, since
144             the canvas::PageManager holds surface and render module via
145             shared_ptr (and makes sure all surfaces are deleted before its
146             render module member goes out of scope).
147         */
148         class DXSurface : public canvas::ISurface
149         {
150         public:
151             DXSurface( DXRenderModule&           rRenderModule,
152                        const ::basegfx::B2ISize& rSize );
153             ~DXSurface();
154 
155             virtual bool selectTexture();
156             virtual bool isValid();
157             virtual bool update( const ::basegfx::B2IPoint& rDestPos,
158                                 const ::basegfx::B2IRange& rSourceRect,
159                                 ::canvas::IColorBuffer&    rSource );
160             virtual ::basegfx::B2IVector getSize();
161             COMReference<IDirect3DTexture9> getTexture() const;
162 
163         private:
164             /// Guard local methods against concurrent acces to RenderModule
165             class ImplRenderModuleGuard : private ::boost::noncopyable
166             {
167             public:
168                 explicit inline ImplRenderModuleGuard( DXRenderModule& rRenderModule );
169                 inline ~ImplRenderModuleGuard();
170 
171             private:
172                 DXRenderModule& mrRenderModule;
173             };
174 
175             DXRenderModule&                  mrRenderModule;
176             COMReference<IDirect3DTexture9>  mpTexture;
177 
178             ::basegfx::B2IVector             maSize;
179         };
180 
181 
182         //////////////////////////////////////////////////////////////////////////////////
183         // DXRenderModule
184         //////////////////////////////////////////////////////////////////////////////////
185 
186         /// Default implementation of IDXRenderModule
187         class DXRenderModule : public IDXRenderModule
188         {
189         public:
190             explicit DXRenderModule( const ::Window& rWindow );
191             ~DXRenderModule();
192 
lock() const193             virtual void lock() const { maMutex.acquire(); }
unlock() const194             virtual void unlock() const { maMutex.release(); }
195 
196             virtual COMReference<IDirect3DSurface9>
197                 createSystemMemorySurface( const ::basegfx::B2IVector& rSize );
198             virtual void disposing();
getHWND() const199             virtual HWND getHWND() const { return mhWnd; }
200             virtual void screenShot();
201 
202             virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
203                                const ::basegfx::B2IRectangle& rCurrWindowArea );
204 
205             virtual void resize( const ::basegfx::B2IRange& rect );
206             virtual ::basegfx::B2IVector getPageSize();
207             virtual ::canvas::ISurfaceSharedPtr createSurface( const ::basegfx::B2IVector& surfaceSize );
208             virtual void beginPrimitive( PrimitiveType eType );
209             virtual void endPrimitive();
210             virtual void pushVertex( const ::canvas::Vertex& vertex );
211             virtual bool isError();
212 
getDevice()213             COMReference<IDirect3DDevice9> getDevice() { return mpDevice; }
214 
215             void flushVertexCache();
216             void commitVertexCache();
217 
218         private:
219 
220             bool create( const ::Window& rWindow );
221             bool createDevice();
222             bool verifyDevice( const UINT nAdapter );
223             UINT getAdapterFromWindow();
224 
225             /** This object represents the DirectX state machine.  In order
226                 to serialize access to DirectX's global state, a global
227                 mutex is required.
228             */
229             static ::osl::Mutex                         maMutex;
230 
231             HWND                                        mhWnd;
232             COMReference<IDirect3DDevice9>              mpDevice;
233             COMReference<IDirect3D9>                    mpDirect3D9;
234             COMReference<IDirect3DSwapChain9>           mpSwapChain;
235             COMReference<IDirect3DVertexBuffer9>        mpVertexBuffer;
236             ::canvas::ISurfaceSharedPtr                 mpTexture;
237             ::boost::scoped_ptr<SystemChildWindow>      mpWindow;
238             ::basegfx::B2IVector                        maSize;
239             typedef std::vector<canvas::Vertex>         vertexCache_t;
240             vertexCache_t                               maVertexCache;
241             std::size_t                                 mnCount;
242             int                                         mnBeginSceneCount;
243             bool                                        mbCanUseDynamicTextures;
244             bool                                        mbError;
245             PrimitiveType                               meType;
246             ::basegfx::B2IVector                        maPageSize;
247             D3DPRESENT_PARAMETERS                       mad3dpp;
248 
isDisposed() const249             inline bool isDisposed() const { return (mhWnd==NULL); }
250 
251             struct dxvertex
252             {
253                 float x,y,z,rhw;
254                 DWORD diffuse;
255                 float u,v;
256             };
257 
258             std::size_t                                 maNumVertices;
259             std::size_t                                 maWriteIndex;
260             std::size_t                                 maReadIndex;
261         };
262 
263         ::osl::Mutex DXRenderModule::maMutex;
264 
265         //////////////////////////////////////////////////////////////////////////////////
266         // DXSurface::ImplRenderModuleGuard
267         //////////////////////////////////////////////////////////////////////////////////
268 
ImplRenderModuleGuard(DXRenderModule & rRenderModule)269         inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
270             DXRenderModule& rRenderModule ) :
271             mrRenderModule( rRenderModule )
272         {
273             mrRenderModule.lock();
274         }
275 
~ImplRenderModuleGuard()276         inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
277         {
278             mrRenderModule.unlock();
279         }
280 
281 #ifdef FAKE_MAX_NUMBER_TEXTURES
282         static sal_uInt32 gNumSurfaces = 0;
283 #endif
284 
fillRect(sal_uInt32 * pDest,sal_uInt32 dwWidth,sal_uInt32 dwHeight,sal_uInt32 dwPitch,sal_uInt32 dwColor)285         void fillRect( sal_uInt32 *pDest,
286                        sal_uInt32 dwWidth,
287                        sal_uInt32 dwHeight,
288                        sal_uInt32 dwPitch,
289                        sal_uInt32 dwColor )
290         {
291             for(sal_uInt32 i=0; i<dwWidth; ++i)
292             {
293                 pDest[i]=dwColor;
294                 pDest[((dwHeight-1)*dwPitch)+i]=dwColor;
295             }
296 
297             for(sal_uInt32 j=0; j<dwHeight; ++j)
298             {
299                 pDest[0]=dwColor;
300                 pDest[dwWidth-1]=dwColor;
301                 pDest += dwPitch;
302             }
303         }
304 
305         //////////////////////////////////////////////////////////////////////////////////
306         // DXSurface::DXSurface
307         //////////////////////////////////////////////////////////////////////////////////
308 
DXSurface(DXRenderModule & rRenderModule,const::basegfx::B2ISize & rSize)309         DXSurface::DXSurface( DXRenderModule&           rRenderModule,
310                               const ::basegfx::B2ISize& rSize ) :
311             mrRenderModule(rRenderModule),
312             mpTexture(NULL),
313             maSize()
314         {
315             ImplRenderModuleGuard aGuard( mrRenderModule );
316 
317 #ifdef FAKE_MAX_NUMBER_TEXTURES
318             ++gNumSurfaces;
319             if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
320                 return;
321 #endif
322 
323 #ifdef FAKE_MAX_TEXTURE_SIZE
324             if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE)
325                 return;
326             if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE)
327                 return;
328 #endif
329 
330             ENSURE_ARG_OR_THROW(rSize.getX() > 0 && rSize.getY() > 0,
331                             "DXSurface::DXSurface(): request for zero-sized surface");
332 
333             COMReference<IDirect3DDevice9> pDevice(rRenderModule.getDevice());
334 
335             IDirect3DTexture9 *pTexture(NULL);
336             if(FAILED(pDevice->CreateTexture(
337                 rSize.getX(),
338                 rSize.getY(),
339                 1,0,D3DFMT_A8R8G8B8,
340                 D3DPOOL_MANAGED,
341                 &pTexture,NULL)))
342                 return;
343 
344             mpTexture=COMReference<IDirect3DTexture9>(pTexture);
345             maSize = rSize;
346         }
347 
348         //////////////////////////////////////////////////////////////////////////////////
349         // DXSurface::~DXSurface
350         //////////////////////////////////////////////////////////////////////////////////
351 
~DXSurface()352         DXSurface::~DXSurface()
353         {
354             ImplRenderModuleGuard aGuard( mrRenderModule );
355 
356 #ifdef FAKE_MAX_NUMBER_TEXTURES
357             gNumSurfaces--;
358 #endif
359         }
360 
361         //////////////////////////////////////////////////////////////////////////////////
362         // DXSurface::selectTexture
363         //////////////////////////////////////////////////////////////////////////////////
364 
selectTexture()365         bool DXSurface::selectTexture()
366         {
367             ImplRenderModuleGuard aGuard( mrRenderModule );
368             mrRenderModule.flushVertexCache();
369             COMReference<IDirect3DDevice9> pDevice(mrRenderModule.getDevice());
370 
371             if( FAILED(pDevice->SetTexture(0,mpTexture.get())) )
372                 return false;
373 
374             return true;
375         }
376 
377         //////////////////////////////////////////////////////////////////////////////////
378         // DXSurface::isValid
379         //////////////////////////////////////////////////////////////////////////////////
380 
isValid()381         bool DXSurface::isValid()
382         {
383             ImplRenderModuleGuard aGuard( mrRenderModule );
384 
385             if(!(mpTexture.is()))
386                 return false;
387             return true;
388         }
389 
390         //////////////////////////////////////////////////////////////////////////////////
391         // DXSurface::update
392         //////////////////////////////////////////////////////////////////////////////////
393 
update(const::basegfx::B2IPoint & rDestPos,const::basegfx::B2IRange & rSourceRect,::canvas::IColorBuffer & rSource)394         bool DXSurface::update( const ::basegfx::B2IPoint& rDestPos,
395                                 const ::basegfx::B2IRange& rSourceRect,
396                                 ::canvas::IColorBuffer&    rSource )
397         {
398             ImplRenderModuleGuard aGuard( mrRenderModule );
399 
400             // can't update if surface is not valid, that means
401             // either not existent nor restored...
402             if(!(isValid()))
403                 return false;
404 
405             D3DLOCKED_RECT aLockedRect;
406             RECT rect;
407             rect.left = std::max(sal_Int32(0),rDestPos.getX());
408             rect.top =  std::max(sal_Int32(0),rDestPos.getY());
409             // to avoid interpolation artifacts from other textures,
410             // the surface manager allocates one pixel gap between
411             // them. Clear that to transparent.
412             rect.right = std::min(maSize.getX(),
413                                   rect.left + sal_Int32(rSourceRect.getWidth()+1));
414             rect.bottom = std::min(maSize.getY(),
415                                    rect.top + sal_Int32(rSourceRect.getHeight()+1));
416             const bool bClearRightColumn( rect.right < maSize.getX() );
417             const bool bClearBottomRow( rect.bottom < maSize.getY() );
418 
419             if(SUCCEEDED(mpTexture->LockRect(0,&aLockedRect,&rect,D3DLOCK_NOSYSLOCK)))
420             {
421                 if(sal_uInt8* pImage = rSource.lock())
422                 {
423                     switch( rSource.getFormat() )
424                     {
425                         case ::canvas::IColorBuffer::FMT_A8R8G8B8:
426                         {
427                             const std::size_t nSourceBytesPerPixel(4);
428                             const std::size_t nSourcePitchInBytes(rSource.getStride());
429                             pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
430                             pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
431 
432                             // calculate the destination memory address
433                             sal_uInt8 *pDst = (sal_uInt8*)aLockedRect.pBits;
434 
435                             const sal_uInt32 nNumBytesToCopy(
436                                 static_cast<sal_uInt32>(
437                                     rSourceRect.getWidth())*
438                                 nSourceBytesPerPixel);
439                             const sal_uInt64 nNumLines(rSourceRect.getHeight());
440 
441                             for(sal_uInt32 i=0; i<nNumLines; ++i)
442                             {
443                                 rtl_copyMemory(pDst,pImage,nNumBytesToCopy);
444 
445                                 if( bClearRightColumn )
446                                 {
447                                     // to avoid interpolation artifacts
448                                     // from other textures, the surface
449                                     // manager allocates one pixel gap
450                                     // between them. Clear that to
451                                     // transparent.
452                                     pDst[nNumBytesToCopy] =
453                                         pDst[nNumBytesToCopy+1] =
454                                         pDst[nNumBytesToCopy+2] =
455                                         pDst[nNumBytesToCopy+3] = 0x00;
456                                 }
457                                 pDst += aLockedRect.Pitch;
458                                 pImage += nSourcePitchInBytes;
459                             }
460 
461                             if( bClearBottomRow )
462                                 rtl_zeroMemory(pDst,nNumBytesToCopy+4);
463                         }
464                         break;
465 
466                         case ::canvas::IColorBuffer::FMT_R8G8B8:
467                         {
468                             const std::size_t nSourceBytesPerPixel(3);
469                             const std::size_t nSourcePitchInBytes(rSource.getStride());
470                             pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
471                             pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
472 
473                             // calculate the destination memory address
474                             sal_uInt8 *pDst = (sal_uInt8*)aLockedRect.pBits;
475 
476                             const sal_Int32 nNumColumns(
477                                 sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
478                             const sal_Int32 nNumLines(
479                                 sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
480                             for(sal_Int32 i=0; i<nNumLines; ++i)
481                             {
482                                 sal_uInt32 *pDstScanline = reinterpret_cast<sal_uInt32 *>(pDst);
483                                 sal_uInt8 *pSrcScanline = reinterpret_cast<sal_uInt8 *>(pImage);
484 
485                                 for(sal_Int32 x=0; x<nNumColumns; ++x)
486                                 {
487                                     sal_uInt32 color(0xFF000000);
488                                     color |= pSrcScanline[2]<<16;
489                                     color |= pSrcScanline[1]<<8;
490                                     color |= pSrcScanline[0];
491                                     pSrcScanline += 3;
492                                     *pDstScanline++ = color;
493                                 }
494                                 if( bClearRightColumn )
495                                     *pDstScanline++ = 0xFF000000;
496 
497                                 pDst += aLockedRect.Pitch;
498                                 pImage += nSourcePitchInBytes;
499                             }
500 
501                             if( bClearBottomRow )
502                                 rtl_zeroMemory(pDst,4*(nNumColumns+1));
503                         }
504                         break;
505 
506                         case ::canvas::IColorBuffer::FMT_X8R8G8B8:
507                         {
508                             const std::size_t nSourceBytesPerPixel(4);
509                             const std::size_t nSourcePitchInBytes(rSource.getStride());
510                             pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
511                             pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
512 
513                             // calculate the destination memory address
514                             sal_uInt8 *pDst = (sal_uInt8*)aLockedRect.pBits;
515 
516                             const sal_Int32 nNumLines(
517                                 sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
518                             const sal_Int32 nNumColumns(
519                                 sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
520                             for(sal_Int32 i=0; i<nNumLines; ++i)
521                             {
522                                 sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage);
523                                 sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst);
524                                 for(sal_Int32 j=0; j<nNumColumns; ++j)
525                                     pDst32[j] = 0xFF000000 | pSrc32[j];
526 
527                                 if( bClearRightColumn )
528                                     pDst32[nNumColumns] = 0xFF000000;
529 
530                                 pDst += aLockedRect.Pitch;
531                                 pImage += nSourcePitchInBytes;
532                             }
533 
534                             if( bClearBottomRow )
535                                 rtl_zeroMemory(pDst,4*(nNumColumns+1));
536                         }
537                         break;
538 
539                         default:
540                             ENSURE_OR_RETURN_FALSE(false,
541                                             "DXSurface::update(): Unknown/unimplemented buffer format" );
542                             break;
543                     }
544 
545                     rSource.unlock();
546                 }
547 
548                 return SUCCEEDED(mpTexture->UnlockRect(0));
549             }
550 
551             return true;
552         }
553 
554         //////////////////////////////////////////////////////////////////////////////////
555         // DXSurface::getSize
556         //////////////////////////////////////////////////////////////////////////////////
557 
getSize()558         ::basegfx::B2IVector DXSurface::getSize()
559         {
560             return maSize;
561         }
562 
getTexture() const563         COMReference<IDirect3DTexture9> DXSurface::getTexture() const
564         {
565             return mpTexture;
566         }
567 
568         //////////////////////////////////////////////////////////////////////////////////
569         // DXRenderModule::DXRenderModule
570         //////////////////////////////////////////////////////////////////////////////////
571 
DXRenderModule(const::Window & rWindow)572         DXRenderModule::DXRenderModule( const ::Window& rWindow ) :
573             mhWnd(0),
574             mpDevice(),
575             mpDirect3D9(),
576             mpSwapChain(),
577             mpVertexBuffer(),
578             mpTexture(),
579             maSize(),
580             maVertexCache(),
581             mnCount(0),
582             mnBeginSceneCount(0),
583             mbCanUseDynamicTextures(false),
584             mbError( false ),
585             meType( PRIMITIVE_TYPE_UNKNOWN ),
586             maPageSize(),
587             mad3dpp(),
588             maNumVertices( VERTEX_BUFFER_SIZE ),
589             maWriteIndex(0),
590             maReadIndex(0)
591         {
592             // TODO(P2): get rid of those fine-grained locking
593             ::osl::MutexGuard aGuard( maMutex );
594 
595             if(!(create(rWindow)))
596             {
597                 throw lang::NoSupportException(
598                     ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
599                                          "Could not create DirectX device!") ),NULL);
600             }
601 
602             // allocate a single texture surface which can be used later.
603             // we also use this to calibrate the page size.
604             ::basegfx::B2IVector aPageSize(maPageSize);
605             while(true)
606             {
607                 mpTexture = ::canvas::ISurfaceSharedPtr(
608                     new DXSurface(*this,aPageSize));
609                 if(mpTexture->isValid())
610                     break;
611 
612                 aPageSize.setX(aPageSize.getX()>>1);
613                 aPageSize.setY(aPageSize.getY()>>1);
614                 if((aPageSize.getX() < MIN_TEXTURE_SIZE) ||
615                    (aPageSize.getY() < MIN_TEXTURE_SIZE))
616                 {
617                     throw lang::NoSupportException(
618                         ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
619                                             "Could not create DirectX device - "
620                                             "insufficient texture space!") ),NULL);
621                 }
622             }
623             maPageSize=aPageSize;
624 
625             IDirect3DVertexBuffer9 *pVB(NULL);
626             DWORD aFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1);
627             if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
628                                                     D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
629                                                     aFVF,
630                                                     D3DPOOL_DEFAULT,
631                                                     &pVB,
632                                                     NULL)) )
633             {
634                 throw lang::NoSupportException(
635                     ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
636                                          "Could not create DirectX device - out of memory!")),NULL);
637             }
638 
639             mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
640         }
641 
642         //////////////////////////////////////////////////////////////////////////////////
643         // DXRenderModule::~DXRenderModule
644         //////////////////////////////////////////////////////////////////////////////////
645 
~DXRenderModule()646         DXRenderModule::~DXRenderModule()
647         {
648             disposing();
649         }
650 
651         //////////////////////////////////////////////////////////////////////////////////
652         // DXRenderModule::disposing
653         //////////////////////////////////////////////////////////////////////////////////
654 
disposing()655         void DXRenderModule::disposing()
656         {
657             if(!(mhWnd))
658                 return;
659 
660             mpTexture.reset();
661             mpWindow.reset();
662             mhWnd=NULL;
663 
664             // refrain from releasing the DX9 objects. We're the only
665             // ones holding references to them, and it might be
666             // dangerous to destroy the DX9 device, before all other
667             // objects are dead.
668         }
669 
670         //////////////////////////////////////////////////////////////////////////////////
671         // DXRenderModule::create
672         //////////////////////////////////////////////////////////////////////////////////
673 
create(const::Window & rWindow)674         bool DXRenderModule::create( const ::Window& rWindow )
675         {
676             // TODO(P2): get rid of those fine-grained locking
677             ::osl::MutexGuard aGuard( maMutex );
678 
679             maVertexCache.reserve(1024);
680 
681             mpWindow.reset(
682                 new SystemChildWindow(
683                 const_cast<Window *>(&rWindow), 0) );
684 
685             // system child window must not receive mouse events
686             mpWindow->SetMouseTransparent( TRUE );
687 
688             // parent should receive paint messages as well
689             // [PARENTCLIPMODE_NOCLIP], the argument is here
690             // passed as plain numeric value since the stupid
691             // define utilizes a USHORT cast.
692             mpWindow->SetParentClipMode(0x0002);
693 
694             // the system child window must not clear its background
695             mpWindow->EnableEraseBackground( sal_False );
696 
697             mpWindow->SetControlForeground();
698             mpWindow->SetControlBackground();
699             mpWindow->EnablePaint(sal_False);
700 
701             const SystemEnvData *pData = mpWindow->GetSystemData();
702             const HWND hwnd(reinterpret_cast<HWND>(pData->hWnd));
703             mhWnd = const_cast<HWND>(hwnd);
704 
705             ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND>(mhWnd) ),
706                             "DXRenderModule::create() No valid HWND given." );
707 
708             // retrieve position and size of the parent window
709             const ::Size &rSizePixel(rWindow.GetSizePixel());
710 
711             // remember the size of the parent window, since we
712             // need to use this for our child window.
713             maSize.setX(static_cast<sal_Int32>(rSizePixel.Width()));
714             maSize.setY(static_cast<sal_Int32>(rSizePixel.Height()));
715 
716             // let the child window cover the same size as the parent window.
717             mpWindow->SetPosSizePixel(0,0,maSize.getX(),maSize.getY());
718 
719             // TODO(F2): since we would like to share precious hardware
720             // resources, the direct3d9 object should be global. each new
721             // request for a canvas should only create a new swapchain.
722             mpDirect3D9 = COMReference<IDirect3D9>(
723                 Direct3DCreate9(D3D_SDK_VERSION));
724             if(!mpDirect3D9.is())
725                 return false;
726 
727             // create a device from the direct3d9 object.
728             if(!(createDevice()))
729                 return false;
730 
731             mpWindow->Show();
732 
733             return true;
734         }
735 
736         //////////////////////////////////////////////////////////////////////////////////
737         // DXRenderModule::verifyDevice
738         //////////////////////////////////////////////////////////////////////////////////
739 
verifyDevice(const UINT nAdapter)740         bool DXRenderModule::verifyDevice( const UINT nAdapter )
741         {
742             ENSURE_OR_THROW( mpDirect3D9.is(),
743                               "DXRenderModule::verifyDevice() No valid device." );
744 
745             // ask direct3d9 about the capabilities of hardware devices on a specific adapter.
746             // here we decide if the underlying hardware of the machine 'is good enough'.
747             // since we only need a tiny little fraction of what could be used, this
748             // is basically a no-op.
749             D3DCAPS9 aCaps;
750             if(FAILED(mpDirect3D9->GetDeviceCaps(nAdapter,D3DDEVTYPE_HAL,&aCaps)))
751                 return false;
752             if(!(aCaps.MaxTextureWidth))
753                 return false;
754             if(!(aCaps.MaxTextureHeight))
755                 return false;
756             maPageSize = ::basegfx::B2IVector(aCaps.MaxTextureWidth,aCaps.MaxTextureHeight);
757 
758             // check device against white & blacklist entries
759             D3DADAPTER_IDENTIFIER9 aIdent;
760             if(FAILED(mpDirect3D9->GetAdapterIdentifier(nAdapter,0,&aIdent)))
761                 return false;
762 
763             DXCanvasItem aConfigItem;
764             DXCanvasItem::DeviceInfo aInfo;
765             aInfo.nVendorId = aIdent.VendorId;
766             aInfo.nDeviceId = aIdent.DeviceId;
767             aInfo.nDeviceSubSysId = aIdent.SubSysId;
768             aInfo.nDeviceRevision = aIdent.Revision;
769 
770             aInfo.nDriverId = HIWORD(aIdent.DriverVersion.HighPart);
771             aInfo.nDriverVersion = LOWORD(aIdent.DriverVersion.HighPart);
772             aInfo.nDriverSubVersion = HIWORD(aIdent.DriverVersion.LowPart);
773             aInfo.nDriverBuildId = LOWORD(aIdent.DriverVersion.LowPart);
774 
775             if( !aConfigItem.isDeviceUsable(aInfo) )
776                 return false;
777 
778             if( aConfigItem.isBlacklistCurrentDevice() )
779             {
780                 aConfigItem.blacklistDevice(aInfo);
781                 return false;
782             }
783 
784             aConfigItem.adaptMaxTextureSize(maPageSize);
785 
786             mbCanUseDynamicTextures = (aCaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0;
787 
788             return true;
789         }
790 
791 
792         //////////////////////////////////////////////////////////////////////////////////
793         // DXRenderModule::createDevice
794         //////////////////////////////////////////////////////////////////////////////////
795 
createDevice()796         bool DXRenderModule::createDevice()
797         {
798             // we expect that the caller provides us with a valid HWND
799             ENSURE_OR_THROW( IsWindow(mhWnd),
800                               "DXRenderModule::createDevice() No valid HWND given." );
801 
802             // we expect that the caller already created the direct3d9 object.
803             ENSURE_OR_THROW( mpDirect3D9.is(),
804                               "DXRenderModule::createDevice() no direct3d?." );
805 
806             // find the adapter identifier from the window.
807             const UINT aAdapter(getAdapterFromWindow());
808             if(aAdapter == static_cast<UINT>(-1))
809                 return false;
810 
811             // verify that device possibly works
812             if( !verifyDevice(aAdapter) )
813                 return false;
814 
815             // query the display mode from the selected adapter.
816             // we'll later request the backbuffer format to be same
817             // same as the display format.
818             D3DDISPLAYMODE d3ddm;
819             mpDirect3D9->GetAdapterDisplayMode(aAdapter,&d3ddm);
820 
821             // we need to use D3DSWAPEFFECT_COPY here since the canvas-api has
822             // basically nothing to do with efficient resource handling. it tries
823             // to avoid drawing whenevery possible, which is simply not the most
824             // efficient way we could leverage the hardware in this case. it would
825             // be far better to redraw the backbuffer each time we would like to
826             // display the content of the backbuffer, but we need to face reality
827             // here and follow how the canvas was designed.
828 
829             // Strictly speaking, we don't need a full screen worth of
830             // backbuffer here. We could also scale dynamically with
831             // the current window size, but this will make it
832             // necessary to temporarily have two buffers while copying
833             // from the old to the new one. What's more, at the time
834             // we need a larger buffer, DX might not have sufficient
835             // resources available, and we're then left with too small
836             // a back buffer, and no way of falling back to a
837             // different canvas implementation.
838             ZeroMemory( &mad3dpp, sizeof(mad3dpp) );
839             mad3dpp.BackBufferWidth = std::max(sal_Int32(maSize.getX()),
840                                                sal_Int32(d3ddm.Width));
841             mad3dpp.BackBufferHeight = std::max(sal_Int32(maSize.getY()),
842                                                 sal_Int32(d3ddm.Height));
843             mad3dpp.BackBufferCount = 1;
844             mad3dpp.Windowed = TRUE;
845             mad3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
846             mad3dpp.BackBufferFormat = d3ddm.Format;
847             mad3dpp.EnableAutoDepthStencil = FALSE;
848             mad3dpp.hDeviceWindow = mhWnd;
849             mad3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
850 
851             // now create the device, first try hardware vertex processing,
852             // then software vertex processing. if both queries fail, we give up
853             // and indicate failure.
854             IDirect3DDevice9 *pDevice(NULL);
855             if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
856                                                 D3DDEVTYPE_HAL,
857                                                 mhWnd,
858                                                 D3DCREATE_HARDWARE_VERTEXPROCESSING|
859                                                 D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
860                                                 &mad3dpp,
861                                                 &pDevice)))
862                 if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
863                                                     D3DDEVTYPE_HAL,
864                                                     mhWnd,
865                                                     D3DCREATE_SOFTWARE_VERTEXPROCESSING|
866                                                     D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
867                                                     &mad3dpp,
868                                                     &pDevice)))
869                     return false;
870 
871             // got it, store it in a safe place...
872             mpDevice=COMReference<IDirect3DDevice9>(pDevice);
873 
874             // After CreateDevice, the first swap chain already exists, so just get it...
875             IDirect3DSwapChain9 *pSwapChain(NULL);
876             pDevice->GetSwapChain(0,&pSwapChain);
877             mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
878             if( !mpSwapChain.is() )
879                 return false;
880 
881             // clear the render target [which is the backbuffer in this case].
882             // we are forced to do this once, and furthermore right now.
883             // please note that this is only possible since we created the
884             // backbuffer with copy semantics [the content is preserved after
885             // calls to Present()], which is an unnecessarily expensive operation.
886             LPDIRECT3DSURFACE9 pBackBuffer = NULL;
887             mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
888             mpDevice->SetRenderTarget( 0, pBackBuffer );
889             mpDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0L);
890             pBackBuffer->Release();
891 
892             return true;
893         }
894 
895         //////////////////////////////////////////////////////////////////////////////////
896         // DXRenderModule::createSystemMemorySurface
897         //////////////////////////////////////////////////////////////////////////////////
898 
createSystemMemorySurface(const::basegfx::B2IVector & rSize)899         COMReference<IDirect3DSurface9> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
900         {
901             if(isDisposed())
902                 return COMReference<IDirect3DSurface9>(NULL);
903 
904             // please note that D3DFMT_X8R8G8B8 is the only format we're
905             // able to choose here, since GetDC() doesn't support any
906             // other 32bit-format.
907             IDirect3DSurface9 *pSurface(NULL);
908             if( FAILED(mpDevice->CreateOffscreenPlainSurface(
909                            rSize.getX(),
910                            rSize.getY(),
911                            D3DFMT_X8R8G8B8,
912                            D3DPOOL_SYSTEMMEM,
913                            &pSurface,
914                            NULL)) )
915             {
916                 throw lang::NoSupportException(
917                     ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
918                                          "Could not create offscreen surface - out of mem!") ),NULL);
919             }
920 
921             return COMReference<IDirect3DSurface9>(pSurface);
922         }
923 
924         //////////////////////////////////////////////////////////////////////////////////
925         // DXRenderModule::flip
926         //////////////////////////////////////////////////////////////////////////////////
927 
flip(const::basegfx::B2IRectangle & rUpdateArea,const::basegfx::B2IRectangle &)928         bool DXRenderModule::flip( const ::basegfx::B2IRectangle& rUpdateArea,
929                                    const ::basegfx::B2IRectangle& /*rCurrWindowArea*/ )
930         {
931             // TODO(P2): get rid of those fine-grained locking
932             ::osl::MutexGuard aGuard( maMutex );
933 
934             if(isDisposed() || !mpSwapChain.is())
935                 return false;
936 
937             flushVertexCache();
938 
939             // TODO(P2): Might be faster to actually pass update area here
940             RECT aRect =
941                 {
942                     rUpdateArea.getMinX(),
943                     rUpdateArea.getMinY(),
944                     rUpdateArea.getMaxX(),
945                     rUpdateArea.getMaxY()
946                 };
947             HRESULT hr(mpSwapChain->Present(&aRect,&aRect,NULL,NULL,0));
948             if(FAILED(hr))
949             {
950                 if(hr != D3DERR_DEVICELOST)
951                     return false;
952 
953                 // interestingly enough, sometimes the Reset() below
954                 // *still* causes DeviceLost errors. So, cycle until
955                 // DX was kind enough to really reset the device...
956                 do
957                 {
958                     mpVertexBuffer.reset();
959                     hr = mpDevice->Reset(&mad3dpp);
960                     if(SUCCEEDED(hr))
961                     {
962                         IDirect3DVertexBuffer9 *pVB(NULL);
963                         DWORD aFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1);
964                         if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
965                                                                 D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
966                                                                 aFVF,
967                                                                 D3DPOOL_DEFAULT,
968                                                                 &pVB,
969                                                                 NULL)) )
970                         {
971                             throw lang::NoSupportException(
972                                 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
973                                                      "Could not create DirectX device - out of memory!")),NULL);
974                         }
975                         mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
976 
977                         // retry after the restore
978                         if(SUCCEEDED(mpSwapChain->Present(&aRect,&aRect,NULL,NULL,0)))
979                             return true;
980                     }
981 
982                     TimeValue aTimeout;
983                     aTimeout.Seconds=1;
984                     aTimeout.Nanosec=0;
985                     osl_waitThread(&aTimeout);
986                 }
987                 while(hr == D3DERR_DEVICELOST);
988 
989                 return false;
990             }
991 
992             return true;
993         }
994 
995         //////////////////////////////////////////////////////////////////////////////////
996         // DXRenderModule::screenShot
997         //////////////////////////////////////////////////////////////////////////////////
998 
screenShot()999         void DXRenderModule::screenShot()
1000         {
1001         }
1002 
1003         //////////////////////////////////////////////////////////////////////////////////
1004         // DXRenderModule::resize
1005         //////////////////////////////////////////////////////////////////////////////////
1006 
resize(const::basegfx::B2IRange & rect)1007         void DXRenderModule::resize( const ::basegfx::B2IRange& rect )
1008         {
1009             // TODO(P2): get rid of those fine-grained locking
1010             ::osl::MutexGuard aGuard( maMutex );
1011 
1012             if(isDisposed())
1013                 return;
1014 
1015             // don't do anything if the size didn't change.
1016             if(maSize.getX() == static_cast<sal_Int32>(rect.getWidth()) &&
1017                maSize.getY() == static_cast<sal_Int32>(rect.getHeight()))
1018                return;
1019 
1020             // TODO(Q2): use numeric cast to prevent overflow
1021             maSize.setX(static_cast<sal_Int32>(rect.getWidth()));
1022             maSize.setY(static_cast<sal_Int32>(rect.getHeight()));
1023 
1024             mpWindow->SetPosSizePixel(0,0,maSize.getX(),maSize.getY());
1025 
1026             // resize back buffer, if necessary
1027             // -------------------------------------------------------------
1028 
1029             // don't attempt to create anything if the
1030             // requested size is NULL.
1031             if(!(maSize.getX()))
1032                 return;
1033             if(!(maSize.getY()))
1034                 return;
1035 
1036             // backbuffer too small (might happen, if window is
1037             // maximized across multiple monitors)
1038             if( sal_Int32(mad3dpp.BackBufferWidth) < maSize.getX() ||
1039                 sal_Int32(mad3dpp.BackBufferHeight) < maSize.getY() )
1040             {
1041                 mad3dpp.BackBufferWidth = maSize.getX();
1042                 mad3dpp.BackBufferHeight = maSize.getY();
1043 
1044                 // clear before, save resources
1045                 mpSwapChain.reset();
1046 
1047                 IDirect3DSwapChain9 *pSwapChain(NULL);
1048                 if(FAILED(mpDevice->CreateAdditionalSwapChain(&mad3dpp,&pSwapChain)))
1049                     return;
1050                 mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
1051 
1052                 // clear the render target [which is the backbuffer in this case].
1053                 // we are forced to do this once, and furthermore right now.
1054                 // please note that this is only possible since we created the
1055                 // backbuffer with copy semantics [the content is preserved after
1056                 // calls to Present()], which is an unnecessarily expensive operation.
1057                 LPDIRECT3DSURFACE9 pBackBuffer = NULL;
1058                 mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
1059                 mpDevice->SetRenderTarget( 0, pBackBuffer );
1060                 mpDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0L);
1061                 pBackBuffer->Release();
1062             }
1063         }
1064 
1065         //////////////////////////////////////////////////////////////////////////////////
1066         // DXRenderModule::getPageSize
1067         //////////////////////////////////////////////////////////////////////////////////
1068 
getPageSize()1069         ::basegfx::B2IVector DXRenderModule::getPageSize()
1070         {
1071             // TODO(P2): get rid of those fine-grained locking
1072             ::osl::MutexGuard aGuard( maMutex );
1073             return maPageSize;
1074         }
1075 
1076         //////////////////////////////////////////////////////////////////////////////////
1077         // DXRenderModule::createSurface
1078         //////////////////////////////////////////////////////////////////////////////////
1079 
createSurface(const::basegfx::B2IVector & surfaceSize)1080         ::canvas::ISurfaceSharedPtr DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
1081         {
1082             // TODO(P2): get rid of those fine-grained locking
1083             ::osl::MutexGuard aGuard( maMutex );
1084 
1085             if(isDisposed())
1086                 return ::canvas::ISurfaceSharedPtr();
1087 
1088             const ::basegfx::B2IVector& rPageSize( getPageSize() );
1089             ::basegfx::B2ISize aSize(surfaceSize);
1090             if(!(aSize.getX()))
1091                 aSize.setX(rPageSize.getX());
1092             if(!(aSize.getY()))
1093                 aSize.setY(rPageSize.getY());
1094 
1095             if(mpTexture.use_count() == 1)
1096                 return mpTexture;
1097 
1098             return ::canvas::ISurfaceSharedPtr( new DXSurface(*this,aSize) );
1099         }
1100 
1101         //////////////////////////////////////////////////////////////////////////////////
1102         // DXRenderModule::beginPrimitive
1103         //////////////////////////////////////////////////////////////////////////////////
1104 
beginPrimitive(PrimitiveType eType)1105         void DXRenderModule::beginPrimitive( PrimitiveType eType )
1106         {
1107             // TODO(P2): get rid of those fine-grained locking
1108             ::osl::MutexGuard aGuard( maMutex );
1109 
1110             if(isDisposed())
1111                 return;
1112 
1113             ENSURE_OR_THROW( !mnBeginSceneCount,
1114                               "DXRenderModule::beginPrimitive(): nested call" );
1115 
1116             ++mnBeginSceneCount;
1117             meType=eType;
1118             mnCount=0;
1119         }
1120 
1121         //////////////////////////////////////////////////////////////////////////////////
1122         // DXRenderModule::endPrimitive
1123         //////////////////////////////////////////////////////////////////////////////////
1124 
endPrimitive()1125         void DXRenderModule::endPrimitive()
1126         {
1127             // TODO(P2): get rid of those fine-grained locking
1128             ::osl::MutexGuard aGuard( maMutex );
1129 
1130             if(isDisposed())
1131                 return;
1132 
1133             --mnBeginSceneCount;
1134             meType=PRIMITIVE_TYPE_UNKNOWN;
1135             mnCount=0;
1136         }
1137 
1138         //////////////////////////////////////////////////////////////////////////////////
1139         // DXRenderModule::pushVertex
1140         //////////////////////////////////////////////////////////////////////////////////
1141 
pushVertex(const::canvas::Vertex & vertex)1142         void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex )
1143         {
1144             // TODO(P2): get rid of those fine-grained locking
1145             ::osl::MutexGuard aGuard( maMutex );
1146 
1147             if(isDisposed())
1148                 return;
1149 
1150             switch(meType)
1151             {
1152                 case PRIMITIVE_TYPE_TRIANGLE:
1153                 {
1154                     maVertexCache.push_back(vertex);
1155                     ++mnCount;
1156                     mnCount &= 3;
1157                     break;
1158                 }
1159 
1160                 case PRIMITIVE_TYPE_QUAD:
1161                 {
1162                     if(mnCount == 3)
1163                     {
1164                         const std::size_t size(maVertexCache.size());
1165                         ::canvas::Vertex v0(maVertexCache[size-1]);
1166                         ::canvas::Vertex v2(maVertexCache[size-3]);
1167                         maVertexCache.push_back(v0);
1168                         maVertexCache.push_back(vertex);
1169                         maVertexCache.push_back(v2);
1170                         mnCount=0;
1171                     }
1172                     else
1173                     {
1174                         maVertexCache.push_back(vertex);
1175                         ++mnCount;
1176                     }
1177                     break;
1178                 }
1179 
1180                 default:
1181                     OSL_ENSURE(false,
1182                                "DXRenderModule::pushVertex(): unexpected primitive type");
1183                     break;
1184             }
1185         }
1186 
1187         //////////////////////////////////////////////////////////////////////////////////
1188         // DXRenderModule::isError
1189         //////////////////////////////////////////////////////////////////////////////////
1190 
isError()1191         bool DXRenderModule::isError()
1192         {
1193             // TODO(P2): get rid of those fine-grained locking
1194             ::osl::MutexGuard aGuard( maMutex );
1195 
1196             return mbError;
1197         }
1198 
1199         //////////////////////////////////////////////////////////////////////////////////
1200         // DXRenderModule::getAdapterFromWindow
1201         //////////////////////////////////////////////////////////////////////////////////
1202 
getAdapterFromWindow()1203         UINT DXRenderModule::getAdapterFromWindow()
1204         {
1205             HMONITOR hMonitor(aMonitorSupport.MonitorFromWindow(mhWnd));
1206             UINT aAdapterCount(mpDirect3D9->GetAdapterCount());
1207             for(UINT i=0; i<aAdapterCount; ++i)
1208                 if(hMonitor == mpDirect3D9->GetAdapterMonitor(i))
1209                     return i;
1210             return static_cast<UINT>(-1);
1211         }
1212 
1213         //////////////////////////////////////////////////////////////////////////////////
1214         // DXRenderModule::commitVertexCache
1215         //////////////////////////////////////////////////////////////////////////////////
1216 
commitVertexCache()1217         void DXRenderModule::commitVertexCache()
1218         {
1219             if(maReadIndex != maWriteIndex)
1220             {
1221                 const std::size_t nVertexStride = sizeof(dxvertex);
1222                 const unsigned int nNumVertices = maWriteIndex-maReadIndex;
1223                 const unsigned int nNumPrimitives = nNumVertices / 3;
1224 
1225                 if(FAILED(mpDevice->SetStreamSource(0,mpVertexBuffer.get(),0,nVertexStride)))
1226                     return;
1227 
1228                 if(FAILED(mpDevice->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)))
1229                     return;
1230 
1231                 if(FAILED(mpDevice->BeginScene()))
1232                     return;
1233 
1234                 mbError |= FAILED(mpDevice->DrawPrimitive(D3DPT_TRIANGLELIST,maReadIndex,nNumPrimitives));
1235                 mbError |= FAILED(mpDevice->EndScene());
1236 
1237                 maReadIndex += nNumVertices;
1238             }
1239         }
1240 
1241         //////////////////////////////////////////////////////////////////////////////////
1242         // DXRenderModule::flushVertexCache
1243         //////////////////////////////////////////////////////////////////////////////////
1244 
flushVertexCache()1245         void DXRenderModule::flushVertexCache()
1246         {
1247             if(!(maVertexCache.size()))
1248                 return;
1249 
1250             mbError=true;
1251 
1252             if( FAILED(mpDevice->SetRenderState(D3DRS_LIGHTING,FALSE)))
1253                 return;
1254 
1255             // enable texture alpha blending
1256             if( FAILED(mpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE)))
1257                 return;
1258 
1259             mpDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
1260             mpDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
1261             mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSU ,D3DTADDRESS_CLAMP );
1262             mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSV ,D3DTADDRESS_CLAMP );
1263 
1264             // configure the fixed-function pipeline.
1265             // the only 'feature' we need here is to modulate the alpha-channels
1266             // from the texture and the interpolated diffuse color. the result
1267             // will then be blended with the backbuffer.
1268             // fragment color = texture color * diffuse.alpha.
1269             mpDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
1270             mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
1271             mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
1272 
1273             // normal combination of object...
1274             if( FAILED(mpDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA)) )
1275                 return;
1276 
1277             // ..and background color
1278             if( FAILED(mpDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA)) )
1279                 return;
1280 
1281             // disable backface culling; this enables us to mirror sprites
1282             // by simply reverting the triangles, which, with enabled
1283             // culling, would be invisible otherwise
1284             if( FAILED(mpDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE)) )
1285                 return;
1286 
1287             mbError=false;
1288 
1289             std::size_t nSize(maVertexCache.size());
1290             const std::size_t nVertexStride = sizeof(dxvertex);
1291 
1292             const ::basegfx::B2IVector aPageSize(getPageSize());
1293             const float nHalfPixelSizeX(0.5f/aPageSize.getX());
1294             const float nHalfPixelSizeY(0.5f/aPageSize.getY());
1295             vertexCache_t::const_iterator it(maVertexCache.begin());
1296 
1297             while( nSize )
1298             {
1299                 DWORD dwLockFlags(D3DLOCK_NOOVERWRITE);
1300 
1301                 // Check to see if there's space for the current set of
1302                 // vertices in the buffer.
1303                 if( maNumVertices - maWriteIndex < nSize )
1304                 {
1305                     commitVertexCache();
1306                     dwLockFlags = D3DLOCK_DISCARD;
1307                     maWriteIndex = 0;
1308                     maReadIndex = 0;
1309                 }
1310 
1311                 dxvertex *vertices(NULL);
1312                 const std::size_t nNumVertices(
1313                     std::min(maNumVertices - maWriteIndex,
1314                              nSize));
1315                 if(FAILED(mpVertexBuffer->Lock(maWriteIndex*nVertexStride,
1316                                                nNumVertices*nVertexStride,
1317                                                (void **)&vertices,
1318                                                dwLockFlags)))
1319                     return;
1320 
1321                 std::size_t nIndex(0);
1322                 while( nIndex < nNumVertices )
1323                 {
1324                     dxvertex &dest = vertices[nIndex++];
1325                     dest.x=it->x;
1326                     dest.y=it->y;
1327                     dest.z=it->z;
1328                     dest.rhw=1;
1329                     const sal_uInt32 alpha(static_cast<sal_uInt32>(it->a*255.0f));
1330                     dest.diffuse=D3DCOLOR_ARGB(alpha,255,255,255);
1331                     dest.u=static_cast<float>(it->u + nHalfPixelSizeX);
1332                     dest.v=static_cast<float>(it->v + nHalfPixelSizeY);
1333                     ++it;
1334                 }
1335 
1336                 mpVertexBuffer->Unlock();
1337 
1338                 // Advance to the next position in the vertex buffer.
1339                 maWriteIndex += nNumVertices;
1340                 nSize -= nNumVertices;
1341 
1342                 commitVertexCache();
1343             }
1344 
1345             maVertexCache.clear();
1346         }
1347     }
1348 
1349     //////////////////////////////////////////////////////////////////////////////////
1350     // createRenderModule
1351     //////////////////////////////////////////////////////////////////////////////////
1352 
createRenderModule(const::Window & rParent)1353     IDXRenderModuleSharedPtr createRenderModule( const ::Window& rParent )
1354     {
1355         return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) );
1356     }
1357 }
1358 
1359 #endif
1360