LibreOffice Module canvas (master)  1
dx_9rm.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 
23 #include <string.h>
24 
30 #include <com/sun/star/lang/NoSupportException.hpp>
31 #include <osl/thread.hxx>
32 #include <osl/time.h>
33 #include <tools/diagnose_ex.h>
34 #include <vcl/syschild.hxx>
35 #include <vcl/sysdata.hxx>
36 #include <vcl/window.hxx>
37 
38 #include <canvas/elapsedtime.hxx>
39 #include <canvas/canvastools.hxx>
43 
44 #include "dx_config.hxx"
45 #include "dx_impltools.hxx"
46 #include "dx_rendermodule.hxx"
47 
48 #define MIN_TEXTURE_SIZE (32)
49 //#define FAKE_MAX_NUMBER_TEXTURES (2)
50 //#define FAKE_MAX_TEXTURE_SIZE (4096)
51 
52 #define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
53  // vertex buffer (must be divisable
54  // by 3, as each triangle primitive
55  // has 3 vertices)
56 
57 
58 using namespace ::com::sun::star;
59 
60 
61 // 'dxcanvas' namespace
62 
63 
64 namespace dxcanvas
65 {
66  namespace
67  {
68  class DXRenderModule;
69 
70 
71  // DXSurface
72 
73 
82  class DXSurface : public canvas::ISurface
83  {
84  public:
85  DXSurface( DXRenderModule& rRenderModule,
86  const ::basegfx::B2ISize& rSize );
87  ~DXSurface() override;
88 
89  virtual bool selectTexture() override;
90  virtual bool isValid() override;
91  virtual bool update( const ::basegfx::B2IPoint& rDestPos,
92  const ::basegfx::B2IRange& rSourceRect,
93  ::canvas::IColorBuffer& rSource ) override;
94  virtual ::basegfx::B2IVector getSize();
95 
96  private:
98  class ImplRenderModuleGuard
99  {
100  public:
102  ImplRenderModuleGuard(const ImplRenderModuleGuard&) = delete;
103  const ImplRenderModuleGuard& operator=(const ImplRenderModuleGuard&) = delete;
104 
105  explicit ImplRenderModuleGuard( DXRenderModule& rRenderModule );
106  ~ImplRenderModuleGuard();
107 
108  private:
109  DXRenderModule& mrRenderModule;
110  };
111 
112  DXRenderModule& mrRenderModule;
113  COMReference<IDirect3DTexture9> mpTexture;
114 
116  };
117 
118 
119  // DXRenderModule
120 
121 
123  class DXRenderModule final: public IDXRenderModule
124  {
125  public:
126  explicit DXRenderModule( const vcl::Window& rWindow );
127  ~DXRenderModule() override;
128 
129  virtual void lock() const override { maMutex.acquire(); }
130  virtual void unlock() const override { maMutex.release(); }
131 
132  virtual COMReference<IDirect3DSurface9>
133  createSystemMemorySurface( const ::basegfx::B2IVector& rSize ) override;
134  virtual void disposing() override;
135  virtual HWND getHWND() const override { return mhWnd; }
136  virtual void screenShot() override;
137 
138  virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
139  const ::basegfx::B2IRectangle& rCurrWindowArea ) override;
140 
141  virtual void resize( const ::basegfx::B2IRange& rect ) override;
142  virtual ::basegfx::B2IVector getPageSize() override;
143  virtual std::shared_ptr<canvas::ISurface> createSurface( const ::basegfx::B2IVector& surfaceSize ) override;
144  virtual void beginPrimitive( PrimitiveType eType ) override;
145  virtual void endPrimitive() override;
146  virtual void pushVertex( const ::canvas::Vertex& vertex ) override;
147  virtual bool isError() override;
148 
149  COMReference<IDirect3DDevice9> getDevice() { return mpDevice; }
150 
151  void flushVertexCache();
152  void commitVertexCache();
153 
154  private:
155 
156  bool create( const vcl::Window& rWindow );
157  bool createDevice();
158  bool verifyDevice( const UINT nAdapter );
159  UINT getAdapterFromWindow();
160 
165  static ::osl::Mutex maMutex;
166 
167  HWND mhWnd;
168  COMReference<IDirect3DDevice9> mpDevice;
169  COMReference<IDirect3D9> mpDirect3D9;
170  COMReference<IDirect3DSwapChain9> mpSwapChain;
171  COMReference<IDirect3DVertexBuffer9> mpVertexBuffer;
172  std::shared_ptr<canvas::ISurface> mpTexture;
175  typedef std::vector<canvas::Vertex> vertexCache_t;
176  vertexCache_t maVertexCache;
177  std::size_t mnCount;
180  bool mbError;
181  PrimitiveType meType;
183  D3DPRESENT_PARAMETERS mad3dpp;
184 
185  bool isDisposed() const { return (mhWnd==nullptr); }
186 
187  struct dxvertex
188  {
189  float x,y,z,rhw;
190  DWORD diffuse;
191  float u,v;
192  };
193 
194  std::size_t maNumVertices;
195  std::size_t maWriteIndex;
196  std::size_t maReadIndex;
197  };
198 
199  ::osl::Mutex DXRenderModule::maMutex;
200 
201 
202  // DXSurface::ImplRenderModuleGuard
203 
204 
205  DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
206  DXRenderModule& rRenderModule ) :
207  mrRenderModule( rRenderModule )
208  {
209  mrRenderModule.lock();
210  }
211 
212  DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
213  {
214  mrRenderModule.unlock();
215  }
216 
217 #ifdef FAKE_MAX_NUMBER_TEXTURES
218  static sal_uInt32 gNumSurfaces = 0;
219 #endif
220 
221  // DXSurface::DXSurface
222 
223 
224  DXSurface::DXSurface( DXRenderModule& rRenderModule,
225  const ::basegfx::B2ISize& rSize ) :
226  mrRenderModule(rRenderModule),
227  mpTexture(nullptr),
228  maSize()
229  {
230  ImplRenderModuleGuard aGuard( mrRenderModule );
231 
232 #ifdef FAKE_MAX_NUMBER_TEXTURES
233  ++gNumSurfaces;
234  if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
235  return;
236 #endif
237 
238 #ifdef FAKE_MAX_TEXTURE_SIZE
239  if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE)
240  return;
241  if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE)
242  return;
243 #endif
244 
245  ENSURE_ARG_OR_THROW(rSize.getX() > 0 && rSize.getY() > 0,
246  "DXSurface::DXSurface(): request for zero-sized surface");
247 
248  COMReference<IDirect3DDevice9> pDevice(rRenderModule.getDevice());
249 
250  IDirect3DTexture9 *pTexture(nullptr);
251  if(FAILED(pDevice->CreateTexture(
252  rSize.getX(),
253  rSize.getY(),
254  1,0,D3DFMT_A8R8G8B8,
255  D3DPOOL_MANAGED,
256  &pTexture,nullptr)))
257  return;
258 
259  mpTexture=COMReference<IDirect3DTexture9>(pTexture);
260  maSize = rSize;
261  }
262 
263 
264  // DXSurface::~DXSurface
265 
266 
267  DXSurface::~DXSurface()
268  {
269  ImplRenderModuleGuard aGuard( mrRenderModule );
270 
271 #ifdef FAKE_MAX_NUMBER_TEXTURES
272  gNumSurfaces--;
273 #endif
274  }
275 
276 
277  // DXSurface::selectTexture
278 
279 
280  bool DXSurface::selectTexture()
281  {
282  ImplRenderModuleGuard aGuard( mrRenderModule );
283  mrRenderModule.flushVertexCache();
284  COMReference<IDirect3DDevice9> pDevice(mrRenderModule.getDevice());
285 
286  if( FAILED(pDevice->SetTexture(0,mpTexture.get())) )
287  return false;
288 
289  return true;
290  }
291 
292 
293  // DXSurface::isValid
294 
295 
296  bool DXSurface::isValid()
297  {
298  ImplRenderModuleGuard aGuard( mrRenderModule );
299 
300  if(!(mpTexture.is()))
301  return false;
302  return true;
303  }
304 
305 
306  // DXSurface::update
307 
308 
309  bool DXSurface::update( const ::basegfx::B2IPoint& rDestPos,
310  const ::basegfx::B2IRange& rSourceRect,
311  ::canvas::IColorBuffer& rSource )
312  {
313  ImplRenderModuleGuard aGuard( mrRenderModule );
314 
315  // can't update if surface is not valid, that means
316  // either not existent nor restored...
317  if(!(isValid()))
318  return false;
319 
320  D3DLOCKED_RECT aLockedRect;
321  RECT rect;
322  rect.left = std::max(sal_Int32(0),rDestPos.getX());
323  rect.top = std::max(sal_Int32(0),rDestPos.getY());
324  // to avoid interpolation artifacts from other textures,
325  // the surface manager allocates one pixel gap between
326  // them. Clear that to transparent.
327  rect.right = std::min(maSize.getX(),
328  rect.left + sal_Int32(rSourceRect.getWidth()+1));
329  rect.bottom = std::min(maSize.getY(),
330  rect.top + sal_Int32(rSourceRect.getHeight()+1));
331  const bool bClearRightColumn( rect.right < maSize.getX() );
332  const bool bClearBottomRow( rect.bottom < maSize.getY() );
333 
334  if(SUCCEEDED(mpTexture->LockRect(0,&aLockedRect,&rect,D3DLOCK_NOSYSLOCK)))
335  {
336  if(sal_uInt8* pImage = rSource.lock())
337  {
338  switch( rSource.getFormat() )
339  {
340  case ::canvas::IColorBuffer::Format::A8R8G8B8:
341  {
342  const std::size_t nSourceBytesPerPixel(4);
343  const std::size_t nSourcePitchInBytes(rSource.getStride());
344  pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
345  pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
346 
347  // calculate the destination memory address
348  sal_uInt8 *pDst = static_cast<sal_uInt8*>(aLockedRect.pBits);
349 
350  const sal_uInt32 nNumBytesToCopy(
351  static_cast<sal_uInt32>(
352  rSourceRect.getWidth())*
353  nSourceBytesPerPixel);
354  const sal_uInt64 nNumLines(rSourceRect.getHeight());
355 
356  for(sal_uInt64 i=0; i<nNumLines; ++i)
357  {
358  memcpy(pDst,pImage,nNumBytesToCopy);
359 
360  if( bClearRightColumn )
361  {
362  // to avoid interpolation artifacts
363  // from other textures, the surface
364  // manager allocates one pixel gap
365  // between them. Clear that to
366  // transparent.
367  pDst[nNumBytesToCopy] =
368  pDst[nNumBytesToCopy+1] =
369  pDst[nNumBytesToCopy+2] =
370  pDst[nNumBytesToCopy+3] = 0x00;
371  }
372  pDst += aLockedRect.Pitch;
373  pImage += nSourcePitchInBytes;
374  }
375 
376  if( bClearBottomRow )
377  memset(pDst, 0, nNumBytesToCopy+4);
378  }
379  break;
380 
381  case ::canvas::IColorBuffer::Format::X8R8G8B8:
382  {
383  const std::size_t nSourceBytesPerPixel(4);
384  const std::size_t nSourcePitchInBytes(rSource.getStride());
385  pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
386  pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
387 
388  // calculate the destination memory address
389  sal_uInt8 *pDst = static_cast<sal_uInt8*>(aLockedRect.pBits);
390 
391  const sal_Int32 nNumLines(
392  sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
393  const sal_Int32 nNumColumns(
394  sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
395  for(sal_Int32 i=0; i<nNumLines; ++i)
396  {
397  sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage);
398  sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst);
399  for(sal_Int32 j=0; j<nNumColumns; ++j)
400  pDst32[j] = 0xFF000000 | pSrc32[j];
401 
402  if( bClearRightColumn )
403  pDst32[nNumColumns] = 0xFF000000;
404 
405  pDst += aLockedRect.Pitch;
406  pImage += nSourcePitchInBytes;
407  }
408 
409  if( bClearBottomRow )
410  memset(pDst, 0, 4*(nNumColumns+1));
411  }
412  break;
413 
414  default:
416  "DXSurface::update(): Unknown/unimplemented buffer format" );
417  break;
418  }
419 
420  rSource.unlock();
421  }
422 
423  return SUCCEEDED(mpTexture->UnlockRect(0));
424  }
425 
426  return true;
427  }
428 
429 
430  // DXSurface::getSize
431 
432 
433  ::basegfx::B2IVector DXSurface::getSize()
434  {
435  return maSize;
436  }
437 
438  // DXRenderModule::DXRenderModule
439 
440 
441  DXRenderModule::DXRenderModule( const vcl::Window& rWindow ) :
442  mhWnd(nullptr),
443  mpDevice(),
444  mpDirect3D9(),
445  mpSwapChain(),
446  mpVertexBuffer(),
447  mpTexture(),
448  maSize(),
449  maVertexCache(),
450  mnCount(0),
453  mbError( false ),
454  meType( PrimitiveType::Unknown ),
455  maPageSize(),
456  mad3dpp(),
458  maWriteIndex(0),
459  maReadIndex(0)
460  {
461  // TODO(P2): get rid of those fine-grained locking
462  ::osl::MutexGuard aGuard( maMutex );
463 
464  if(!(create(rWindow)))
465  {
466  throw lang::NoSupportException( "Could not create DirectX device!" );
467  }
468 
469  // allocate a single texture surface which can be used later.
470  // we also use this to calibrate the page size.
471  ::basegfx::B2IVector aPageSize(maPageSize);
472  while(true)
473  {
474  mpTexture = std::shared_ptr<canvas::ISurface>(
475  new DXSurface(*this,aPageSize));
476  if(mpTexture->isValid())
477  break;
478 
479  aPageSize.setX(aPageSize.getX()>>1);
480  aPageSize.setY(aPageSize.getY()>>1);
481  if((aPageSize.getX() < MIN_TEXTURE_SIZE) ||
482  (aPageSize.getY() < MIN_TEXTURE_SIZE))
483  {
484  throw lang::NoSupportException(
485  "Could not create DirectX device - insufficient texture space!" );
486  }
487  }
488  maPageSize=aPageSize;
489 
490  IDirect3DVertexBuffer9 *pVB(nullptr);
491  if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
492  D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
493  D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1,
494  D3DPOOL_DEFAULT,
495  &pVB,
496  nullptr)) )
497  {
498  throw lang::NoSupportException(
499  "Could not create DirectX device - out of memory!" );
500  }
501 
502  mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
503  }
504 
505 
506  // DXRenderModule::~DXRenderModule
507 
508 
509  DXRenderModule::~DXRenderModule()
510  {
511  disposing();
512  }
513 
514 
515  // DXRenderModule::disposing
516 
517 
518  void DXRenderModule::disposing()
519  {
520  if(!mhWnd)
521  return;
522 
523  mpTexture.reset();
525  mhWnd=nullptr;
526 
527  // refrain from releasing the DX9 objects. We're the only
528  // ones holding references to them, and it might be
529  // dangerous to destroy the DX9 device, before all other
530  // objects are dead.
531  }
532 
533 
534  // DXRenderModule::create
535 
536 
537  bool DXRenderModule::create( const vcl::Window& rWindow )
538  {
539  // TODO(P2): get rid of those fine-grained locking
540  ::osl::MutexGuard aGuard( maMutex );
541 
542  // TODO(F2): since we would like to share precious hardware
543  // resources, the direct3d9 object should be global. each new
544  // request for a canvas should only create a new swapchain.
545  mpDirect3D9 = COMReference<IDirect3D9>(
546  Direct3DCreate9(D3D_SDK_VERSION));
547  if(!mpDirect3D9.is())
548  return false;
549 
550  maVertexCache.reserve( 1024 );
551 
554  const_cast<vcl::Window *>(&rWindow), 0) );
555 
556  // system child window must not receive mouse events
557  mpWindow->SetMouseTransparent( TRUE );
558 
559  // parent should receive paint messages as well
560  mpWindow->SetParentClipMode(ParentClipMode::NoClip);
561 
562  // the system child window must not clear its background
564 
567 
568  const SystemEnvData *pData = mpWindow->GetSystemData();
569  const HWND hwnd(reinterpret_cast<HWND>(pData->hWnd));
570  mhWnd = hwnd;
571 
572  ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND>(mhWnd) ),
573  "DXRenderModule::create() No valid HWND given." );
574 
575  // retrieve position and size of the parent window
576  const ::Size &rSizePixel(rWindow.GetSizePixel());
577 
578  // remember the size of the parent window, since we
579  // need to use this for our child window.
580  maSize.setX(static_cast<sal_Int32>(rSizePixel.Width()));
581  maSize.setY(static_cast<sal_Int32>(rSizePixel.Height()));
582 
583  // let the child window cover the same size as the parent window.
585 
586  // create a device from the direct3d9 object.
587  if(!(createDevice()))
588  {
590  return false;
591  }
592 
593  mpWindow->Show();
594 
595  return true;
596  }
597 
598 
599  // DXRenderModule::verifyDevice
600 
601 
602  bool DXRenderModule::verifyDevice( const UINT nAdapter )
603  {
605  "DXRenderModule::verifyDevice() No valid device." );
606 
607  // ask direct3d9 about the capabilities of hardware devices on a specific adapter.
608  // here we decide if the underlying hardware of the machine 'is good enough'.
609  // since we only need a tiny little fraction of what could be used, this
610  // is basically a no-op.
611  D3DCAPS9 aCaps;
612  if(FAILED(mpDirect3D9->GetDeviceCaps(nAdapter,D3DDEVTYPE_HAL,&aCaps)))
613  return false;
614  if(!(aCaps.MaxTextureWidth))
615  return false;
616  if(!(aCaps.MaxTextureHeight))
617  return false;
618  maPageSize = ::basegfx::B2IVector(aCaps.MaxTextureWidth,aCaps.MaxTextureHeight);
619 
620  // check device against white & blacklist entries
621  D3DADAPTER_IDENTIFIER9 aIdent;
622  if(FAILED(mpDirect3D9->GetAdapterIdentifier(nAdapter,0,&aIdent)))
623  return false;
624 
625  DXCanvasItem aConfigItem;
626  DXCanvasItem::DeviceInfo aInfo;
627  aInfo.nVendorId = aIdent.VendorId;
628  aInfo.nDeviceId = aIdent.DeviceId;
629  aInfo.nDeviceSubSysId = aIdent.SubSysId;
630  aInfo.nDeviceRevision = aIdent.Revision;
631 
632  aInfo.nDriverId = HIWORD(aIdent.DriverVersion.HighPart);
633  aInfo.nDriverVersion = LOWORD(aIdent.DriverVersion.HighPart);
634  aInfo.nDriverSubVersion = HIWORD(aIdent.DriverVersion.LowPart);
635  aInfo.nDriverBuildId = LOWORD(aIdent.DriverVersion.LowPart);
636 
637  if( !aConfigItem.isDeviceUsable(aInfo) )
638  return false;
639 
640  if( aConfigItem.isBlacklistCurrentDevice() )
641  {
642  aConfigItem.blacklistDevice(aInfo);
643  return false;
644  }
645 
646  aConfigItem.adaptMaxTextureSize(maPageSize);
647 
648  mbCanUseDynamicTextures = (aCaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0;
649 
650  return true;
651  }
652 
653 
654  // DXRenderModule::createDevice
655 
656 
657  bool DXRenderModule::createDevice()
658  {
659  // we expect that the caller provides us with a valid HWND
660  ENSURE_OR_THROW( IsWindow(mhWnd),
661  "DXRenderModule::createDevice() No valid HWND given." );
662 
663  // we expect that the caller already created the direct3d9 object.
665  "DXRenderModule::createDevice() no direct3d?." );
666 
667  // find the adapter identifier from the window.
668  const UINT aAdapter(getAdapterFromWindow());
669  if(aAdapter == static_cast<UINT>(-1))
670  return false;
671 
672  // verify that device possibly works
673  if( !verifyDevice(aAdapter) )
674  return false;
675 
676  // query the display mode from the selected adapter.
677  // we'll later request the backbuffer format to be same
678  // same as the display format.
679  D3DDISPLAYMODE d3ddm;
680  mpDirect3D9->GetAdapterDisplayMode(aAdapter,&d3ddm);
681 
682  // we need to use D3DSWAPEFFECT_COPY here since the canvas-api has
683  // basically nothing to do with efficient resource handling. it tries
684  // to avoid drawing whenever possible, which is simply not the most
685  // efficient way we could leverage the hardware in this case. it would
686  // be far better to redraw the backbuffer each time we would like to
687  // display the content of the backbuffer, but we need to face reality
688  // here and follow how the canvas was designed.
689 
690  // Strictly speaking, we don't need a full screen worth of
691  // backbuffer here. We could also scale dynamically with
692  // the current window size, but this will make it
693  // necessary to temporarily have two buffers while copying
694  // from the old to the new one. What's more, at the time
695  // we need a larger buffer, DX might not have sufficient
696  // resources available, and we're then left with too small
697  // a back buffer, and no way of falling back to a
698  // different canvas implementation.
699  ZeroMemory( &mad3dpp, sizeof(mad3dpp) );
700  mad3dpp.BackBufferWidth = std::max(maSize.getX(),
701  sal_Int32(d3ddm.Width));
702  mad3dpp.BackBufferHeight = std::max(maSize.getY(),
703  sal_Int32(d3ddm.Height));
704  mad3dpp.BackBufferCount = 1;
705  mad3dpp.Windowed = TRUE;
706  mad3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
707  mad3dpp.BackBufferFormat = d3ddm.Format;
708  mad3dpp.EnableAutoDepthStencil = FALSE;
709  mad3dpp.hDeviceWindow = mhWnd;
710  mad3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
711 
712  // now create the device, first try hardware vertex processing,
713  // then software vertex processing. if both queries fail, we give up
714  // and indicate failure.
715  IDirect3DDevice9 *pDevice(nullptr);
716  if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
717  D3DDEVTYPE_HAL,
718  mhWnd,
719  D3DCREATE_HARDWARE_VERTEXPROCESSING|
720  D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
721  &mad3dpp,
722  &pDevice)))
723  if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
724  D3DDEVTYPE_HAL,
725  mhWnd,
726  D3DCREATE_SOFTWARE_VERTEXPROCESSING|
727  D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
728  &mad3dpp,
729  &pDevice)))
730  return false;
731 
732  // got it, store it in a safe place...
733  mpDevice=COMReference<IDirect3DDevice9>(pDevice);
734 
735  // After CreateDevice, the first swap chain already exists, so just get it...
736  IDirect3DSwapChain9 *pSwapChain(nullptr);
737  pDevice->GetSwapChain(0,&pSwapChain);
738  mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
739  if( !mpSwapChain.is() )
740  return false;
741 
742  // clear the render target [which is the backbuffer in this case].
743  // we are forced to do this once, and furthermore right now.
744  // please note that this is only possible since we created the
745  // backbuffer with copy semantics [the content is preserved after
746  // calls to Present()], which is an unnecessarily expensive operation.
747  LPDIRECT3DSURFACE9 pBackBuffer = nullptr;
748  mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
749  mpDevice->SetRenderTarget( 0, pBackBuffer );
750  mpDevice->Clear(0,nullptr,D3DCLEAR_TARGET,0,1.0f,0);
751  pBackBuffer->Release();
752 
753  return true;
754  }
755 
756 
757  // DXRenderModule::createSystemMemorySurface
758 
759 
760  COMReference<IDirect3DSurface9> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
761  {
762  if(isDisposed())
763  return COMReference<IDirect3DSurface9>(nullptr);
764 
765  // please note that D3DFMT_X8R8G8B8 is the only format we're
766  // able to choose here, since GetDC() doesn't support any
767  // other 32bit-format.
768  IDirect3DSurface9 *pSurface(nullptr);
769  if( FAILED(mpDevice->CreateOffscreenPlainSurface(
770  rSize.getX(),
771  rSize.getY(),
772  D3DFMT_X8R8G8B8,
773  D3DPOOL_SYSTEMMEM,
774  &pSurface,
775  nullptr)) )
776  {
777  throw lang::NoSupportException(
778  "Could not create offscreen surface - out of mem!" );
779  }
780 
781  return COMReference<IDirect3DSurface9>(pSurface);
782  }
783 
784 
785  // DXRenderModule::flip
786 
787 
788  bool DXRenderModule::flip( const ::basegfx::B2IRectangle& rUpdateArea,
789  const ::basegfx::B2IRectangle& /*rCurrWindowArea*/ )
790  {
791  // TODO(P2): get rid of those fine-grained locking
792  ::osl::MutexGuard aGuard( maMutex );
793 
794  if(isDisposed() || !mpSwapChain.is())
795  return false;
796 
797  flushVertexCache();
798 
799  // TODO(P2): Might be faster to actually pass update area here
800  RECT aRect =
801  {
802  rUpdateArea.getMinX(),
803  rUpdateArea.getMinY(),
804  rUpdateArea.getMaxX(),
805  rUpdateArea.getMaxY()
806  };
807  HRESULT hr(mpSwapChain->Present(&aRect,&aRect,nullptr,nullptr,0));
808  if(FAILED(hr))
809  {
810  if(hr != D3DERR_DEVICELOST)
811  return false;
812 
813  // interestingly enough, sometimes the Reset() below
814  // *still* causes DeviceLost errors. So, cycle until
815  // DX was kind enough to really reset the device...
816  do
817  {
818  mpVertexBuffer.reset();
819  hr = mpDevice->Reset(&mad3dpp);
820  if(SUCCEEDED(hr))
821  {
822  IDirect3DVertexBuffer9 *pVB(nullptr);
823  if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
824  D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
825  D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1,
826  D3DPOOL_DEFAULT,
827  &pVB,
828  nullptr)) )
829  {
830  throw lang::NoSupportException(
831  "Could not create DirectX device - out of memory!" );
832  }
833  mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
834 
835  // retry after the restore
836  if(SUCCEEDED(mpSwapChain->Present(&aRect,&aRect,nullptr,nullptr,0)))
837  return true;
838  }
839 
840  osl::Thread::wait(std::chrono::seconds(1));
841  }
842  while(hr == D3DERR_DEVICELOST);
843 
844  return false;
845  }
846 
847  return true;
848  }
849 
850 
851  // DXRenderModule::screenShot
852 
853 
854  void DXRenderModule::screenShot()
855  {
856  }
857 
858 
859  // DXRenderModule::resize
860 
861 
862  void DXRenderModule::resize( const ::basegfx::B2IRange& rect )
863  {
864  // TODO(P2): get rid of those fine-grained locking
865  ::osl::MutexGuard aGuard( maMutex );
866 
867  if(isDisposed())
868  return;
869 
870  // don't do anything if the size didn't change.
871  if(maSize.getX() == static_cast<sal_Int32>(rect.getWidth()) &&
872  maSize.getY() == static_cast<sal_Int32>(rect.getHeight()))
873  return;
874 
875  // TODO(Q2): use numeric cast to prevent overflow
876  maSize.setX(static_cast<sal_Int32>(rect.getWidth()));
877  maSize.setY(static_cast<sal_Int32>(rect.getHeight()));
878 
880 
881  // resize back buffer, if necessary
882 
883 
884  // don't attempt to create anything if the
885  // requested size is NULL.
886  if(!(maSize.getX()))
887  return;
888  if(!(maSize.getY()))
889  return;
890 
891  // backbuffer too small (might happen, if window is
892  // maximized across multiple monitors)
893  if( sal_Int32(mad3dpp.BackBufferWidth) < maSize.getX() ||
894  sal_Int32(mad3dpp.BackBufferHeight) < maSize.getY() )
895  {
896  mad3dpp.BackBufferWidth = maSize.getX();
897  mad3dpp.BackBufferHeight = maSize.getY();
898 
899  // clear before, save resources
900  mpSwapChain.reset();
901 
902  IDirect3DSwapChain9 *pSwapChain(nullptr);
903  if(FAILED(mpDevice->CreateAdditionalSwapChain(&mad3dpp,&pSwapChain)))
904  return;
905  mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
906 
907  // clear the render target [which is the backbuffer in this case].
908  // we are forced to do this once, and furthermore right now.
909  // please note that this is only possible since we created the
910  // backbuffer with copy semantics [the content is preserved after
911  // calls to Present()], which is an unnecessarily expensive operation.
912  LPDIRECT3DSURFACE9 pBackBuffer = nullptr;
913  mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
914  mpDevice->SetRenderTarget( 0, pBackBuffer );
915  mpDevice->Clear(0,nullptr,D3DCLEAR_TARGET,0,1.0f,0);
916  pBackBuffer->Release();
917  }
918  }
919 
920 
921  // DXRenderModule::getPageSize
922 
923 
924  ::basegfx::B2IVector DXRenderModule::getPageSize()
925  {
926  // TODO(P2): get rid of those fine-grained locking
927  ::osl::MutexGuard aGuard( maMutex );
928  return maPageSize;
929  }
930 
931 
932  // DXRenderModule::createSurface
933 
934 
935  std::shared_ptr<canvas::ISurface> DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
936  {
937  // TODO(P2): get rid of those fine-grained locking
938  ::osl::MutexGuard aGuard( maMutex );
939 
940  if(isDisposed())
941  return std::shared_ptr<canvas::ISurface>();
942 
943  const ::basegfx::B2IVector& rPageSize( getPageSize() );
944  ::basegfx::B2ISize aSize(surfaceSize);
945  if(!(aSize.getX()))
946  aSize.setX(rPageSize.getX());
947  if(!(aSize.getY()))
948  aSize.setY(rPageSize.getY());
949 
950  if(mpTexture.use_count() == 1)
951  return mpTexture;
952 
953  return std::shared_ptr<canvas::ISurface>( new DXSurface(*this,aSize) );
954  }
955 
956 
957  // DXRenderModule::beginPrimitive
958 
959 
960  void DXRenderModule::beginPrimitive( PrimitiveType eType )
961  {
962  // TODO(P2): get rid of those fine-grained locking
963  ::osl::MutexGuard aGuard( maMutex );
964 
965  if(isDisposed())
966  return;
967 
969  "DXRenderModule::beginPrimitive(): nested call" );
970 
972  meType=eType;
973  mnCount=0;
974  }
975 
976 
977  // DXRenderModule::endPrimitive
978 
979 
980  void DXRenderModule::endPrimitive()
981  {
982  // TODO(P2): get rid of those fine-grained locking
983  ::osl::MutexGuard aGuard( maMutex );
984 
985  if(isDisposed())
986  return;
987 
989  meType = PrimitiveType::Unknown;
990  mnCount = 0;
991  }
992 
993 
994  // DXRenderModule::pushVertex
995 
996 
997  void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex )
998  {
999  // TODO(P2): get rid of those fine-grained locking
1000  ::osl::MutexGuard aGuard( maMutex );
1001 
1002  if(isDisposed())
1003  return;
1004 
1005  switch(meType)
1006  {
1007  case PrimitiveType::Triangle:
1008  {
1009  maVertexCache.push_back(vertex);
1010  ++mnCount;
1011  mnCount &= 3;
1012  break;
1013  }
1014 
1015  case PrimitiveType::Quad:
1016  {
1017  if(mnCount == 3)
1018  {
1019  const std::size_t size(maVertexCache.size());
1020  ::canvas::Vertex v0(maVertexCache[size-1]);
1021  ::canvas::Vertex v2(maVertexCache[size-3]);
1022  maVertexCache.push_back(v0);
1023  maVertexCache.push_back(vertex);
1024  maVertexCache.push_back(v2);
1025  mnCount=0;
1026  }
1027  else
1028  {
1029  maVertexCache.push_back(vertex);
1030  ++mnCount;
1031  }
1032  break;
1033  }
1034 
1035  default:
1036  SAL_WARN("canvas.directx", "DXRenderModule::pushVertex(): unexpected primitive type");
1037  break;
1038  }
1039  }
1040 
1041 
1042  // DXRenderModule::isError
1043 
1044 
1045  bool DXRenderModule::isError()
1046  {
1047  // TODO(P2): get rid of those fine-grained locking
1048  ::osl::MutexGuard aGuard( maMutex );
1049 
1050  return mbError;
1051  }
1052 
1053 
1054  // DXRenderModule::getAdapterFromWindow
1055 
1056 
1057  UINT DXRenderModule::getAdapterFromWindow()
1058  {
1059  HMONITOR hMonitor(MonitorFromWindow(mhWnd, MONITOR_DEFAULTTONEAREST));
1060  UINT aAdapterCount(mpDirect3D9->GetAdapterCount());
1061  for(UINT i=0; i<aAdapterCount; ++i)
1062  if(hMonitor == mpDirect3D9->GetAdapterMonitor(i))
1063  return i;
1064  return static_cast<UINT>(-1);
1065  }
1066 
1067 
1068  // DXRenderModule::commitVertexCache
1069 
1070 
1071  void DXRenderModule::commitVertexCache()
1072  {
1073  if(maReadIndex != maWriteIndex)
1074  {
1075  const std::size_t nVertexStride = sizeof(dxvertex);
1076  const unsigned int nNumVertices = maWriteIndex-maReadIndex;
1077  const unsigned int nNumPrimitives = nNumVertices / 3;
1078 
1079  if(FAILED(mpDevice->SetStreamSource(0,mpVertexBuffer.get(),0,nVertexStride)))
1080  return;
1081 
1082  if(FAILED(mpDevice->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)))
1083  return;
1084 
1085  if(FAILED(mpDevice->BeginScene()))
1086  return;
1087 
1088  mbError |= FAILED(mpDevice->DrawPrimitive(D3DPT_TRIANGLELIST,maReadIndex,nNumPrimitives));
1089  mbError |= FAILED(mpDevice->EndScene());
1090 
1091  maReadIndex += nNumVertices;
1092  }
1093  }
1094 
1095 
1096  // DXRenderModule::flushVertexCache
1097 
1098 
1099  void DXRenderModule::flushVertexCache()
1100  {
1101  if(maVertexCache.empty())
1102  return;
1103 
1104  mbError=true;
1105 
1106  if( FAILED(mpDevice->SetRenderState(D3DRS_LIGHTING,FALSE)))
1107  return;
1108 
1109  // enable texture alpha blending
1110  if( FAILED(mpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE)))
1111  return;
1112 
1113  mpDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
1114  mpDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
1115  mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSU ,D3DTADDRESS_CLAMP );
1116  mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSV ,D3DTADDRESS_CLAMP );
1117 
1118  // configure the fixed-function pipeline.
1119  // the only 'feature' we need here is to modulate the alpha-channels
1120  // from the texture and the interpolated diffuse color. the result
1121  // will then be blended with the backbuffer.
1122  // fragment color = texture color * diffuse.alpha.
1123  mpDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
1124  mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
1125  mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
1126 
1127  // normal combination of object...
1128  if( FAILED(mpDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA)) )
1129  return;
1130 
1131  // ..and background color
1132  if( FAILED(mpDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA)) )
1133  return;
1134 
1135  // disable backface culling; this enables us to mirror sprites
1136  // by simply reverting the triangles, which, with enabled
1137  // culling, would be invisible otherwise
1138  if( FAILED(mpDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE)) )
1139  return;
1140 
1141  mbError=false;
1142 
1143  std::size_t nSize(maVertexCache.size());
1144  const std::size_t nVertexStride = sizeof(dxvertex);
1145 
1146  const ::basegfx::B2IVector aPageSize(getPageSize());
1147  const float nHalfPixelSizeX(0.5f/aPageSize.getX());
1148  const float nHalfPixelSizeY(0.5f/aPageSize.getY());
1149  vertexCache_t::const_iterator it(maVertexCache.begin());
1150 
1151  while( nSize )
1152  {
1153  DWORD dwLockFlags(D3DLOCK_NOOVERWRITE);
1154 
1155  // Check to see if there's space for the current set of
1156  // vertices in the buffer.
1157  if( maNumVertices - maWriteIndex < nSize )
1158  {
1159  commitVertexCache();
1160  dwLockFlags = D3DLOCK_DISCARD;
1161  maWriteIndex = 0;
1162  maReadIndex = 0;
1163  }
1164 
1165  dxvertex *vertices(nullptr);
1166  const std::size_t nNumVertices(
1167  std::min(maNumVertices - maWriteIndex,
1168  nSize));
1169  if(FAILED(mpVertexBuffer->Lock(maWriteIndex*nVertexStride,
1170  nNumVertices*nVertexStride,
1171  reinterpret_cast<void **>(&vertices),
1172  dwLockFlags)))
1173  return;
1174 
1175  std::size_t nIndex(0);
1176  while( nIndex < nNumVertices )
1177  {
1178  dxvertex &dest = vertices[nIndex++];
1179  dest.x=it->x;
1180  dest.y=it->y;
1181  dest.z=it->z;
1182  dest.rhw=1;
1183  const sal_uInt32 alpha(static_cast<sal_uInt32>(it->a*255.0f));
1184  dest.diffuse=D3DCOLOR_ARGB(alpha,255,255,255);
1185  dest.u=static_cast<float>(it->u + nHalfPixelSizeX);
1186  dest.v=static_cast<float>(it->v + nHalfPixelSizeY);
1187  ++it;
1188  }
1189 
1190  mpVertexBuffer->Unlock();
1191 
1192  // Advance to the next position in the vertex buffer.
1193  maWriteIndex += nNumVertices;
1194  nSize -= nNumVertices;
1195 
1196  commitVertexCache();
1197  }
1198 
1199  maVertexCache.clear();
1200  }
1201  }
1202 
1203 
1204  // createRenderModule
1205 
1206 
1208  {
1209  return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) );
1210  }
1211 }
1212 
1213 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
int mnBeginSceneCount
Definition: dx_9rm.cxx:178
::basegfx::B2IVector maPageSize
Definition: dx_9rm.cxx:182
std::size_t maNumVertices
Definition: dx_9rm.cxx:194
COMReference< IDirect3DSwapChain9 > mpSwapChain
Definition: dx_9rm.cxx:170
virtual sal_uInt8 * lock() const =0
Get a pointer to the raw memory bits of the pixel.
PrimitiveType meType
Definition: dx_9rm.cxx:181
bool update()
EXTERN_C BOOL BOOL const wchar_t *pProgramPath HRESULT hr
RECT
bool mbError
Definition: dx_9rm.cxx:180
std::shared_ptr< IDXRenderModule > IDXRenderModuleSharedPtr
void setY(sal_Int32 fY)
DXRenderModule & mrRenderModule
Definition: dx_9rm.cxx:109
virtual Size GetSizePixel() const
#define max(a, b)
Definition: dx_winstuff.hxx:46
#define MIN_TEXTURE_SIZE
Definition: dx_9rm.cxx:48
void SetParentClipMode(ParentClipMode nMode=ParentClipMode::NONE)
COMReference< IDirect3D9 > mpDirect3D9
Definition: dx_9rm.cxx:169
VclPtr< SystemChildWindow > mpWindow
Definition: dx_9rm.cxx:173
float x
Definition: dx_9rm.cxx:189
Unknown
virtual Format getFormat() const =0
Get format of the color buffer.
virtual void setPosSizePixel(long nX, long nY, long nWidth, long nHeight, PosSizeFlags nFlags=PosSizeFlags::All)
bool mbCanUseDynamicTextures
Definition: dx_9rm.cxx:179
#define min(a, b)
Definition: dx_winstuff.hxx:49
COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:168
std::size_t maReadIndex
Definition: dx_9rm.cxx:196
float rhw
Definition: dx_9rm.cxx:189
virtual const SystemEnvData * GetSystemData() const override
HWND mhWnd
Definition: dx_9rm.cxx:167
Reference< deployment::XPackageRegistry > create(Reference< deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, Reference< XComponentContext > const &xComponentContext)
void EnableEraseBackground(bool bEnable)
#define ENSURE_OR_RETURN_FALSE(c, m)
float y
Definition: dx_9rm.cxx:189
vertexCache_t maVertexCache
Definition: dx_9rm.cxx:176
#define TRUE
D3DPRESENT_PARAMETERS mad3dpp
Definition: dx_9rm.cxx:183
sal_Int32 getX() const
IDXRenderModuleSharedPtr createRenderModule(const vcl::Window &rParent)
Factory method, to create an IRenderModule instance for the given VCL window instance.
Definition: dx_9rm.cxx:1207
void SetControlBackground()
void SetControlForeground()
int i
float u
Definition: dx_9rm.cxx:191
size
#define VERTEX_BUFFER_SIZE
Definition: dx_9rm.cxx:52
std::size_t mnCount
Definition: dx_9rm.cxx:177
#define ENSURE_ARG_OR_THROW(c, m)
virtual sal_uInt32 getStride() const =0
Offset, in bytes, between consecutive scan lines of the bitmap.
void reset(SystemChildWindow *pBody)
#define ENSURE_OR_THROW(c, m)
float v
Definition: dx_9rm.cxx:191
void SetMouseTransparent(bool bTransparent)
unsigned char sal_uInt8
DWORD diffuse
Definition: dx_9rm.cxx:190
float z
Definition: dx_9rm.cxx:189
sal_Int32 getY() const
#define FALSE
#define SAL_WARN(area, stream)
Interface for a raw memory pixel container.
COMReference< IDirect3DVertexBuffer9 > mpVertexBuffer
Definition: dx_9rm.cxx:171
void setX(sal_Int32 fX)
::osl::Mutex maMutex
This object represents the DirectX state machine.
Definition: dx_9rm.cxx:165
COMReference< IDirect3DTexture9 > mpTexture
Definition: dx_9rm.cxx:113
std::size_t maWriteIndex
Definition: dx_9rm.cxx:195
std::shared_ptr< osl::Mutex > const & lock()
virtual void unlock() const =0
unlock previous locked buffer
::basegfx::B2IVector maSize
Definition: dx_9rm.cxx:115
void Show(bool bVisible=true, ShowFlags nFlags=ShowFlags::NONE)