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 divisible
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::make_shared<DXSurface>(*this,aPageSize);
475  if(mpTexture->isValid())
476  break;
477 
478  aPageSize.setX(aPageSize.getX()>>1);
479  aPageSize.setY(aPageSize.getY()>>1);
480  if((aPageSize.getX() < MIN_TEXTURE_SIZE) ||
481  (aPageSize.getY() < MIN_TEXTURE_SIZE))
482  {
483  throw lang::NoSupportException(
484  "Could not create DirectX device - insufficient texture space!" );
485  }
486  }
487  maPageSize=aPageSize;
488 
489  IDirect3DVertexBuffer9 *pVB(nullptr);
490  if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
491  D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
492  D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1,
493  D3DPOOL_DEFAULT,
494  &pVB,
495  nullptr)) )
496  {
497  throw lang::NoSupportException(
498  "Could not create DirectX device - out of memory!" );
499  }
500 
501  mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
502  }
503 
504 
505  // DXRenderModule::~DXRenderModule
506 
507 
508  DXRenderModule::~DXRenderModule()
509  {
510  disposing();
511  }
512 
513 
514  // DXRenderModule::disposing
515 
516 
517  void DXRenderModule::disposing()
518  {
519  if(!mhWnd)
520  return;
521 
522  mpTexture.reset();
524  mhWnd=nullptr;
525 
526  // refrain from releasing the DX9 objects. We're the only
527  // ones holding references to them, and it might be
528  // dangerous to destroy the DX9 device, before all other
529  // objects are dead.
530  }
531 
532 
533  // DXRenderModule::create
534 
535 
536  bool DXRenderModule::create( const vcl::Window& rWindow )
537  {
538  // TODO(P2): get rid of those fine-grained locking
539  ::osl::MutexGuard aGuard( maMutex );
540 
541  // TODO(F2): since we would like to share precious hardware
542  // resources, the direct3d9 object should be global. each new
543  // request for a canvas should only create a new swapchain.
544  mpDirect3D9 = COMReference<IDirect3D9>(
545  Direct3DCreate9(D3D_SDK_VERSION));
546  if(!mpDirect3D9.is())
547  return false;
548 
549  maVertexCache.reserve( 1024 );
550 
553  const_cast<vcl::Window *>(&rWindow), 0) );
554 
555  // system child window must not receive mouse events
556  mpWindow->SetMouseTransparent( true );
557 
558  // parent should receive paint messages as well
559  mpWindow->SetParentClipMode(ParentClipMode::NoClip);
560 
561  // the system child window must not clear its background
563 
566 
568  const HWND hwnd(reinterpret_cast<HWND>(pData->hWnd));
569  mhWnd = hwnd;
570 
571  ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND>(mhWnd) ),
572  "DXRenderModule::create() No valid HWND given." );
573 
574  // retrieve position and size of the parent window
575  const ::Size &rSizePixel(rWindow.GetSizePixel());
576 
577  // remember the size of the parent window, since we
578  // need to use this for our child window.
579  maSize.setX(static_cast<sal_Int32>(rSizePixel.Width()));
580  maSize.setY(static_cast<sal_Int32>(rSizePixel.Height()));
581 
582  // let the child window cover the same size as the parent window.
584 
585  // create a device from the direct3d9 object.
586  if(!(createDevice()))
587  {
589  return false;
590  }
591 
592  mpWindow->Show();
593 
594  return true;
595  }
596 
597 
598  // DXRenderModule::verifyDevice
599 
600 
601  bool DXRenderModule::verifyDevice( const UINT nAdapter )
602  {
604  "DXRenderModule::verifyDevice() No valid device." );
605 
606  // ask direct3d9 about the capabilities of hardware devices on a specific adapter.
607  // here we decide if the underlying hardware of the machine 'is good enough'.
608  // since we only need a tiny little fraction of what could be used, this
609  // is basically a no-op.
610  D3DCAPS9 aCaps;
611  if(FAILED(mpDirect3D9->GetDeviceCaps(nAdapter,D3DDEVTYPE_HAL,&aCaps)))
612  return false;
613  if(!(aCaps.MaxTextureWidth))
614  return false;
615  if(!(aCaps.MaxTextureHeight))
616  return false;
617  maPageSize = ::basegfx::B2IVector(aCaps.MaxTextureWidth,aCaps.MaxTextureHeight);
618 
619  // check device against white & blacklist entries
620  D3DADAPTER_IDENTIFIER9 aIdent;
621  if(FAILED(mpDirect3D9->GetAdapterIdentifier(nAdapter,0,&aIdent)))
622  return false;
623 
624  DXCanvasItem aConfigItem;
625  DXCanvasItem::DeviceInfo aInfo;
626  aInfo.nVendorId = aIdent.VendorId;
627  aInfo.nDeviceId = aIdent.DeviceId;
628  aInfo.nDeviceSubSysId = aIdent.SubSysId;
629  aInfo.nDeviceRevision = aIdent.Revision;
630 
631  aInfo.nDriverId = HIWORD(aIdent.DriverVersion.HighPart);
632  aInfo.nDriverVersion = LOWORD(aIdent.DriverVersion.HighPart);
633  aInfo.nDriverSubVersion = HIWORD(aIdent.DriverVersion.LowPart);
634  aInfo.nDriverBuildId = LOWORD(aIdent.DriverVersion.LowPart);
635 
636  if( !aConfigItem.isDeviceUsable(aInfo) )
637  return false;
638 
639  if( aConfigItem.isBlacklistCurrentDevice() )
640  {
641  aConfigItem.blacklistDevice(aInfo);
642  return false;
643  }
644 
645  aConfigItem.adaptMaxTextureSize(maPageSize);
646 
647  mbCanUseDynamicTextures = (aCaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0;
648 
649  return true;
650  }
651 
652 
653  // DXRenderModule::createDevice
654 
655 
656  bool DXRenderModule::createDevice()
657  {
658  // we expect that the caller provides us with a valid HWND
659  ENSURE_OR_THROW( IsWindow(mhWnd),
660  "DXRenderModule::createDevice() No valid HWND given." );
661 
662  // we expect that the caller already created the direct3d9 object.
664  "DXRenderModule::createDevice() no direct3d?." );
665 
666  // find the adapter identifier from the window.
667  const UINT aAdapter(getAdapterFromWindow());
668  if(aAdapter == static_cast<UINT>(-1))
669  return false;
670 
671  // verify that device possibly works
672  if( !verifyDevice(aAdapter) )
673  return false;
674 
675  // query the display mode from the selected adapter.
676  // we'll later request the backbuffer format to be same
677  // same as the display format.
678  D3DDISPLAYMODE d3ddm;
679  mpDirect3D9->GetAdapterDisplayMode(aAdapter,&d3ddm);
680 
681  // we need to use D3DSWAPEFFECT_COPY here since the canvas-api has
682  // basically nothing to do with efficient resource handling. it tries
683  // to avoid drawing whenever possible, which is simply not the most
684  // efficient way we could leverage the hardware in this case. it would
685  // be far better to redraw the backbuffer each time we would like to
686  // display the content of the backbuffer, but we need to face reality
687  // here and follow how the canvas was designed.
688 
689  // Strictly speaking, we don't need a full screen worth of
690  // backbuffer here. We could also scale dynamically with
691  // the current window size, but this will make it
692  // necessary to temporarily have two buffers while copying
693  // from the old to the new one. What's more, at the time
694  // we need a larger buffer, DX might not have sufficient
695  // resources available, and we're then left with too small
696  // a back buffer, and no way of falling back to a
697  // different canvas implementation.
698  ZeroMemory( &mad3dpp, sizeof(mad3dpp) );
699  mad3dpp.BackBufferWidth = std::max(maSize.getX(),
700  sal_Int32(d3ddm.Width));
701  mad3dpp.BackBufferHeight = std::max(maSize.getY(),
702  sal_Int32(d3ddm.Height));
703  mad3dpp.BackBufferCount = 1;
704  mad3dpp.Windowed = TRUE;
705  mad3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
706  mad3dpp.BackBufferFormat = d3ddm.Format;
707  mad3dpp.EnableAutoDepthStencil = FALSE;
708  mad3dpp.hDeviceWindow = mhWnd;
709  mad3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
710 
711  // now create the device, first try hardware vertex processing,
712  // then software vertex processing. if both queries fail, we give up
713  // and indicate failure.
714  IDirect3DDevice9 *pDevice(nullptr);
715  if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
716  D3DDEVTYPE_HAL,
717  mhWnd,
718  D3DCREATE_HARDWARE_VERTEXPROCESSING|
719  D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
720  &mad3dpp,
721  &pDevice)))
722  if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
723  D3DDEVTYPE_HAL,
724  mhWnd,
725  D3DCREATE_SOFTWARE_VERTEXPROCESSING|
726  D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
727  &mad3dpp,
728  &pDevice)))
729  return false;
730 
731  // got it, store it in a safe place...
732  mpDevice=COMReference<IDirect3DDevice9>(pDevice);
733 
734  // After CreateDevice, the first swap chain already exists, so just get it...
735  IDirect3DSwapChain9 *pSwapChain(nullptr);
736  pDevice->GetSwapChain(0,&pSwapChain);
737  mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
738  if( !mpSwapChain.is() )
739  return false;
740 
741  // clear the render target [which is the backbuffer in this case].
742  // we are forced to do this once, and furthermore right now.
743  // please note that this is only possible since we created the
744  // backbuffer with copy semantics [the content is preserved after
745  // calls to Present()], which is an unnecessarily expensive operation.
746  LPDIRECT3DSURFACE9 pBackBuffer = nullptr;
747  mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
748  mpDevice->SetRenderTarget( 0, pBackBuffer );
749  mpDevice->Clear(0,nullptr,D3DCLEAR_TARGET,0,1.0f,0);
750  pBackBuffer->Release();
751 
752  return true;
753  }
754 
755 
756  // DXRenderModule::createSystemMemorySurface
757 
758 
759  COMReference<IDirect3DSurface9> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
760  {
761  if(isDisposed())
762  return COMReference<IDirect3DSurface9>(nullptr);
763 
764  // please note that D3DFMT_X8R8G8B8 is the only format we're
765  // able to choose here, since GetDC() doesn't support any
766  // other 32bit-format.
767  IDirect3DSurface9 *pSurface(nullptr);
768  if( FAILED(mpDevice->CreateOffscreenPlainSurface(
769  rSize.getX(),
770  rSize.getY(),
771  D3DFMT_X8R8G8B8,
772  D3DPOOL_SYSTEMMEM,
773  &pSurface,
774  nullptr)) )
775  {
776  throw lang::NoSupportException(
777  "Could not create offscreen surface - out of mem!" );
778  }
779 
780  return COMReference<IDirect3DSurface9>(pSurface);
781  }
782 
783 
784  // DXRenderModule::flip
785 
786 
787  bool DXRenderModule::flip( const ::basegfx::B2IRectangle& rUpdateArea,
788  const ::basegfx::B2IRectangle& /*rCurrWindowArea*/ )
789  {
790  // TODO(P2): get rid of those fine-grained locking
791  ::osl::MutexGuard aGuard( maMutex );
792 
793  if(isDisposed() || !mpSwapChain.is())
794  return false;
795 
796  flushVertexCache();
797 
798  // TODO(P2): Might be faster to actually pass update area here
799  RECT aRect =
800  {
801  rUpdateArea.getMinX(),
802  rUpdateArea.getMinY(),
803  rUpdateArea.getMaxX(),
804  rUpdateArea.getMaxY()
805  };
806  HRESULT hr(mpSwapChain->Present(&aRect,&aRect,nullptr,nullptr,0));
807  if(FAILED(hr))
808  {
809  if(hr != D3DERR_DEVICELOST)
810  return false;
811 
812  // interestingly enough, sometimes the Reset() below
813  // *still* causes DeviceLost errors. So, cycle until
814  // DX was kind enough to really reset the device...
815  do
816  {
817  mpVertexBuffer.reset();
818  hr = mpDevice->Reset(&mad3dpp);
819  if(SUCCEEDED(hr))
820  {
821  IDirect3DVertexBuffer9 *pVB(nullptr);
822  if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
823  D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
824  D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1,
825  D3DPOOL_DEFAULT,
826  &pVB,
827  nullptr)) )
828  {
829  throw lang::NoSupportException(
830  "Could not create DirectX device - out of memory!" );
831  }
832  mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
833 
834  // retry after the restore
835  if(SUCCEEDED(mpSwapChain->Present(&aRect,&aRect,nullptr,nullptr,0)))
836  return true;
837  }
838 
839  osl::Thread::wait(std::chrono::seconds(1));
840  }
841  while(hr == D3DERR_DEVICELOST);
842 
843  return false;
844  }
845 
846  return true;
847  }
848 
849 
850  // DXRenderModule::screenShot
851 
852 
853  void DXRenderModule::screenShot()
854  {
855  }
856 
857 
858  // DXRenderModule::resize
859 
860 
861  void DXRenderModule::resize( const ::basegfx::B2IRange& rect )
862  {
863  // TODO(P2): get rid of those fine-grained locking
864  ::osl::MutexGuard aGuard( maMutex );
865 
866  if(isDisposed())
867  return;
868 
869  // don't do anything if the size didn't change.
870  if(maSize.getX() == static_cast<sal_Int32>(rect.getWidth()) &&
871  maSize.getY() == static_cast<sal_Int32>(rect.getHeight()))
872  return;
873 
874  // TODO(Q2): use numeric cast to prevent overflow
875  maSize.setX(static_cast<sal_Int32>(rect.getWidth()));
876  maSize.setY(static_cast<sal_Int32>(rect.getHeight()));
877 
879 
880  // resize back buffer, if necessary
881 
882 
883  // don't attempt to create anything if the
884  // requested size is NULL.
885  if(!(maSize.getX()))
886  return;
887  if(!(maSize.getY()))
888  return;
889 
890  // backbuffer too small (might happen, if window is
891  // maximized across multiple monitors)
892  if( sal_Int32(mad3dpp.BackBufferWidth) < maSize.getX() ||
893  sal_Int32(mad3dpp.BackBufferHeight) < maSize.getY() )
894  {
895  mad3dpp.BackBufferWidth = maSize.getX();
896  mad3dpp.BackBufferHeight = maSize.getY();
897 
898  // clear before, save resources
899  mpSwapChain.reset();
900 
901  IDirect3DSwapChain9 *pSwapChain(nullptr);
902  if(FAILED(mpDevice->CreateAdditionalSwapChain(&mad3dpp,&pSwapChain)))
903  return;
904  mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
905 
906  // clear the render target [which is the backbuffer in this case].
907  // we are forced to do this once, and furthermore right now.
908  // please note that this is only possible since we created the
909  // backbuffer with copy semantics [the content is preserved after
910  // calls to Present()], which is an unnecessarily expensive operation.
911  LPDIRECT3DSURFACE9 pBackBuffer = nullptr;
912  mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
913  mpDevice->SetRenderTarget( 0, pBackBuffer );
914  mpDevice->Clear(0,nullptr,D3DCLEAR_TARGET,0,1.0f,0);
915  pBackBuffer->Release();
916  }
917  }
918 
919 
920  // DXRenderModule::getPageSize
921 
922 
923  ::basegfx::B2IVector DXRenderModule::getPageSize()
924  {
925  // TODO(P2): get rid of those fine-grained locking
926  ::osl::MutexGuard aGuard( maMutex );
927  return maPageSize;
928  }
929 
930 
931  // DXRenderModule::createSurface
932 
933 
934  std::shared_ptr<canvas::ISurface> DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
935  {
936  // TODO(P2): get rid of those fine-grained locking
937  ::osl::MutexGuard aGuard( maMutex );
938 
939  if(isDisposed())
940  return std::shared_ptr<canvas::ISurface>();
941 
942  const ::basegfx::B2IVector& rPageSize( getPageSize() );
943  ::basegfx::B2ISize aSize(surfaceSize);
944  if(!(aSize.getX()))
945  aSize.setX(rPageSize.getX());
946  if(!(aSize.getY()))
947  aSize.setY(rPageSize.getY());
948 
949  if(mpTexture.use_count() == 1)
950  return mpTexture;
951 
952  return std::make_shared<DXSurface>(*this,aSize);
953  }
954 
955 
956  // DXRenderModule::beginPrimitive
957 
958 
959  void DXRenderModule::beginPrimitive( PrimitiveType eType )
960  {
961  // TODO(P2): get rid of those fine-grained locking
962  ::osl::MutexGuard aGuard( maMutex );
963 
964  if(isDisposed())
965  return;
966 
968  "DXRenderModule::beginPrimitive(): nested call" );
969 
971  meType=eType;
972  mnCount=0;
973  }
974 
975 
976  // DXRenderModule::endPrimitive
977 
978 
979  void DXRenderModule::endPrimitive()
980  {
981  // TODO(P2): get rid of those fine-grained locking
982  ::osl::MutexGuard aGuard( maMutex );
983 
984  if(isDisposed())
985  return;
986 
988  meType = PrimitiveType::Unknown;
989  mnCount = 0;
990  }
991 
992 
993  // DXRenderModule::pushVertex
994 
995 
996  void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex )
997  {
998  // TODO(P2): get rid of those fine-grained locking
999  ::osl::MutexGuard aGuard( maMutex );
1000 
1001  if(isDisposed())
1002  return;
1003 
1004  switch(meType)
1005  {
1006  case PrimitiveType::Triangle:
1007  {
1008  maVertexCache.push_back(vertex);
1009  ++mnCount;
1010  mnCount &= 3;
1011  break;
1012  }
1013 
1014  case PrimitiveType::Quad:
1015  {
1016  if(mnCount == 3)
1017  {
1018  const std::size_t size(maVertexCache.size());
1019  ::canvas::Vertex v0(maVertexCache[size-1]);
1020  ::canvas::Vertex v2(maVertexCache[size-3]);
1021  maVertexCache.push_back(v0);
1022  maVertexCache.push_back(vertex);
1023  maVertexCache.push_back(v2);
1024  mnCount=0;
1025  }
1026  else
1027  {
1028  maVertexCache.push_back(vertex);
1029  ++mnCount;
1030  }
1031  break;
1032  }
1033 
1034  default:
1035  SAL_WARN("canvas.directx", "DXRenderModule::pushVertex(): unexpected primitive type");
1036  break;
1037  }
1038  }
1039 
1040 
1041  // DXRenderModule::isError
1042 
1043 
1044  bool DXRenderModule::isError()
1045  {
1046  // TODO(P2): get rid of those fine-grained locking
1047  ::osl::MutexGuard aGuard( maMutex );
1048 
1049  return mbError;
1050  }
1051 
1052 
1053  // DXRenderModule::getAdapterFromWindow
1054 
1055 
1056  UINT DXRenderModule::getAdapterFromWindow()
1057  {
1058  HMONITOR hMonitor(MonitorFromWindow(mhWnd, MONITOR_DEFAULTTONEAREST));
1059  UINT aAdapterCount(mpDirect3D9->GetAdapterCount());
1060  for(UINT i=0; i<aAdapterCount; ++i)
1061  if(hMonitor == mpDirect3D9->GetAdapterMonitor(i))
1062  return i;
1063  return static_cast<UINT>(-1);
1064  }
1065 
1066 
1067  // DXRenderModule::commitVertexCache
1068 
1069 
1070  void DXRenderModule::commitVertexCache()
1071  {
1072  if(maReadIndex != maWriteIndex)
1073  {
1074  const std::size_t nVertexStride = sizeof(dxvertex);
1075  const unsigned int nNumVertices = maWriteIndex-maReadIndex;
1076  const unsigned int nNumPrimitives = nNumVertices / 3;
1077 
1078  if(FAILED(mpDevice->SetStreamSource(0,mpVertexBuffer.get(),0,nVertexStride)))
1079  return;
1080 
1081  if(FAILED(mpDevice->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)))
1082  return;
1083 
1084  if(FAILED(mpDevice->BeginScene()))
1085  return;
1086 
1087  mbError |= FAILED(mpDevice->DrawPrimitive(D3DPT_TRIANGLELIST,maReadIndex,nNumPrimitives));
1088  mbError |= FAILED(mpDevice->EndScene());
1089 
1090  maReadIndex += nNumVertices;
1091  }
1092  }
1093 
1094 
1095  // DXRenderModule::flushVertexCache
1096 
1097 
1098  void DXRenderModule::flushVertexCache()
1099  {
1100  if(maVertexCache.empty())
1101  return;
1102 
1103  mbError=true;
1104 
1105  if( FAILED(mpDevice->SetRenderState(D3DRS_LIGHTING,FALSE)))
1106  return;
1107 
1108  // enable texture alpha blending
1109  if( FAILED(mpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE)))
1110  return;
1111 
1112  mpDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
1113  mpDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
1114  mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSU ,D3DTADDRESS_CLAMP );
1115  mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSV ,D3DTADDRESS_CLAMP );
1116 
1117  // configure the fixed-function pipeline.
1118  // the only 'feature' we need here is to modulate the alpha-channels
1119  // from the texture and the interpolated diffuse color. the result
1120  // will then be blended with the backbuffer.
1121  // fragment color = texture color * diffuse.alpha.
1122  mpDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
1123  mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
1124  mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
1125 
1126  // normal combination of object...
1127  if( FAILED(mpDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA)) )
1128  return;
1129 
1130  // ..and background color
1131  if( FAILED(mpDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA)) )
1132  return;
1133 
1134  // disable backface culling; this enables us to mirror sprites
1135  // by simply reverting the triangles, which, with enabled
1136  // culling, would be invisible otherwise
1137  if( FAILED(mpDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE)) )
1138  return;
1139 
1140  mbError=false;
1141 
1142  std::size_t nSize(maVertexCache.size());
1143  const std::size_t nVertexStride = sizeof(dxvertex);
1144 
1145  const ::basegfx::B2IVector aPageSize(getPageSize());
1146  const float nHalfPixelSizeX(0.5f/aPageSize.getX());
1147  const float nHalfPixelSizeY(0.5f/aPageSize.getY());
1148  vertexCache_t::const_iterator it(maVertexCache.begin());
1149 
1150  while( nSize )
1151  {
1152  DWORD dwLockFlags(D3DLOCK_NOOVERWRITE);
1153 
1154  // Check to see if there's space for the current set of
1155  // vertices in the buffer.
1156  if( maNumVertices - maWriteIndex < nSize )
1157  {
1158  commitVertexCache();
1159  dwLockFlags = D3DLOCK_DISCARD;
1160  maWriteIndex = 0;
1161  maReadIndex = 0;
1162  }
1163 
1164  dxvertex *vertices(nullptr);
1165  const std::size_t nNumVertices(
1166  std::min(maNumVertices - maWriteIndex,
1167  nSize));
1168  if(FAILED(mpVertexBuffer->Lock(maWriteIndex*nVertexStride,
1169  nNumVertices*nVertexStride,
1170  reinterpret_cast<void **>(&vertices),
1171  dwLockFlags)))
1172  return;
1173 
1174  std::size_t nIndex(0);
1175  while( nIndex < nNumVertices )
1176  {
1177  dxvertex &dest = vertices[nIndex++];
1178  dest.x=it->x;
1179  dest.y=it->y;
1180  dest.z=it->z;
1181  dest.rhw=1;
1182  const sal_uInt32 alpha(static_cast<sal_uInt32>(it->a*255.0f));
1183  dest.diffuse=D3DCOLOR_ARGB(alpha,255,255,255);
1184  dest.u=static_cast<float>(it->u + nHalfPixelSizeX);
1185  dest.v=static_cast<float>(it->v + nHalfPixelSizeY);
1186  ++it;
1187  }
1188 
1189  mpVertexBuffer->Unlock();
1190 
1191  // Advance to the next position in the vertex buffer.
1192  maWriteIndex += nNumVertices;
1193  nSize -= nNumVertices;
1194 
1195  commitVertexCache();
1196  }
1197 
1198  maVertexCache.clear();
1199  }
1200  }
1201 
1202 
1203  // createRenderModule
1204 
1205 
1207  {
1208  return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) );
1209  }
1210 }
1211 
1212 /* 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
sal_Int32 nIndex
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.
std::unique_ptr< ContentProperties > pData
PrimitiveType meType
Definition: dx_9rm.cxx:181
bool update()
EXTERN_C BOOL BOOL const wchar_t *pProgramPath HRESULT hr
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:1206
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)