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 <memory>
24 #include <string.h>
25 
31 #include <com/sun/star/lang/NoSupportException.hpp>
32 #include <osl/thread.hxx>
33 #include <osl/time.h>
34 #include <tools/diagnose_ex.h>
35 #include <vcl/syschild.hxx>
36 #include <vcl/sysdata.hxx>
37 #include <vcl/window.hxx>
38 
39 #include <canvas/elapsedtime.hxx>
40 #include <canvas/canvastools.hxx>
43 #include <rendering/isurface.hxx>
44 
45 #include "dx_config.hxx"
46 #include "dx_impltools.hxx"
47 #include "dx_rendermodule.hxx"
48 
49 #define MIN_TEXTURE_SIZE (32)
50 //#define FAKE_MAX_NUMBER_TEXTURES (2)
51 //#define FAKE_MAX_TEXTURE_SIZE (4096)
52 
53 #define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
54  // vertex buffer (must be divisible
55  // by 3, as each triangle primitive
56  // has 3 vertices)
57 
58 
59 using namespace ::com::sun::star;
60 
61 
62 // 'dxcanvas' namespace
63 
64 
65 namespace dxcanvas
66 {
67  namespace
68  {
69  class DXRenderModule;
70 
71 
72  // DXSurface
73 
74 
83  class DXSurface : public canvas::ISurface
84  {
85  public:
86  DXSurface( DXRenderModule& rRenderModule,
87  const ::basegfx::B2ISize& rSize );
88  ~DXSurface() override;
89 
90  virtual bool selectTexture() override;
91  virtual bool isValid() override;
92  virtual bool update( const ::basegfx::B2IPoint& rDestPos,
93  const ::basegfx::B2IRange& rSourceRect,
94  ::canvas::IColorBuffer& rSource ) override;
95  virtual ::basegfx::B2IVector getSize();
96 
97  private:
99  class ImplRenderModuleGuard
100  {
101  public:
103  ImplRenderModuleGuard(const ImplRenderModuleGuard&) = delete;
104  const ImplRenderModuleGuard& operator=(const ImplRenderModuleGuard&) = delete;
105 
106  explicit ImplRenderModuleGuard( DXRenderModule& rRenderModule );
107  ~ImplRenderModuleGuard();
108 
109  private:
110  DXRenderModule& mrRenderModule;
111  };
112 
113  DXRenderModule& mrRenderModule;
114  COMReference<IDirect3DTexture9> mpTexture;
115 
117  };
118 
119 
120  // DXRenderModule
121 
122 
124  class DXRenderModule final: public IDXRenderModule
125  {
126  public:
127  explicit DXRenderModule( const vcl::Window& rWindow );
128  ~DXRenderModule() override;
129 
130  virtual void lock() const override { maMutex.acquire(); }
131  virtual void unlock() const override { maMutex.release(); }
132 
133  virtual COMReference<IDirect3DSurface9>
134  createSystemMemorySurface( const ::basegfx::B2IVector& rSize ) override;
135  virtual void disposing() override;
136  virtual HWND getHWND() const override { return mhWnd; }
137  virtual void screenShot() override;
138 
139  virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
140  const ::basegfx::B2IRectangle& rCurrWindowArea ) override;
141 
142  virtual void resize( const ::basegfx::B2IRange& rect ) override;
143  virtual ::basegfx::B2IVector getPageSize() override;
144  virtual std::shared_ptr<canvas::ISurface> createSurface( const ::basegfx::B2IVector& surfaceSize ) override;
145  virtual void beginPrimitive( PrimitiveType eType ) override;
146  virtual void endPrimitive() override;
147  virtual void pushVertex( const ::canvas::Vertex& vertex ) override;
148  virtual bool isError() override;
149 
150  COMReference<IDirect3DDevice9> getDevice() { return mpDevice; }
151 
152  void flushVertexCache();
153  void commitVertexCache();
154 
155  private:
156 
157  bool create( const vcl::Window& rWindow );
158  bool createDevice();
159  bool verifyDevice( const UINT nAdapter );
160  UINT getAdapterFromWindow();
161 
166  static ::osl::Mutex maMutex;
167 
168  HWND mhWnd;
169  COMReference<IDirect3DDevice9> mpDevice;
170  COMReference<IDirect3D9> mpDirect3D9;
171  COMReference<IDirect3DSwapChain9> mpSwapChain;
172  COMReference<IDirect3DVertexBuffer9> mpVertexBuffer;
173  std::shared_ptr<canvas::ISurface> mpTexture;
176  typedef std::vector<canvas::Vertex> vertexCache_t;
177  vertexCache_t maVertexCache;
178  std::size_t mnCount;
181  bool mbError;
182  PrimitiveType meType;
184  D3DPRESENT_PARAMETERS mad3dpp;
185 
186  bool isDisposed() const { return (mhWnd==nullptr); }
187 
188  struct dxvertex
189  {
190  float x,y,z,rhw;
191  DWORD diffuse;
192  float u,v;
193  };
194 
195  std::size_t maNumVertices;
196  std::size_t maWriteIndex;
197  std::size_t maReadIndex;
198  };
199 
200  ::osl::Mutex DXRenderModule::maMutex;
201 
202 
203  // DXSurface::ImplRenderModuleGuard
204 
205 
206  DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
207  DXRenderModule& rRenderModule ) :
208  mrRenderModule( rRenderModule )
209  {
210  mrRenderModule.lock();
211  }
212 
213  DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
214  {
215  mrRenderModule.unlock();
216  }
217 
218 #ifdef FAKE_MAX_NUMBER_TEXTURES
219  static sal_uInt32 gNumSurfaces = 0;
220 #endif
221 
222  // DXSurface::DXSurface
223 
224 
225  DXSurface::DXSurface( DXRenderModule& rRenderModule,
226  const ::basegfx::B2ISize& rSize ) :
227  mrRenderModule(rRenderModule),
228  mpTexture(nullptr),
229  maSize()
230  {
231  ImplRenderModuleGuard aGuard( mrRenderModule );
232 
233 #ifdef FAKE_MAX_NUMBER_TEXTURES
234  ++gNumSurfaces;
235  if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
236  return;
237 #endif
238 
239 #ifdef FAKE_MAX_TEXTURE_SIZE
240  if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE)
241  return;
242  if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE)
243  return;
244 #endif
245 
246  ENSURE_ARG_OR_THROW(rSize.getX() > 0 && rSize.getY() > 0,
247  "DXSurface::DXSurface(): request for zero-sized surface");
248 
249  COMReference<IDirect3DDevice9> pDevice(rRenderModule.getDevice());
250 
251  IDirect3DTexture9 *pTexture(nullptr);
252  if(FAILED(pDevice->CreateTexture(
253  rSize.getX(),
254  rSize.getY(),
255  1,0,D3DFMT_A8R8G8B8,
256  D3DPOOL_MANAGED,
257  &pTexture,nullptr)))
258  return;
259 
260  mpTexture=COMReference<IDirect3DTexture9>(pTexture);
261  maSize = rSize;
262  }
263 
264 
265  // DXSurface::~DXSurface
266 
267 
268  DXSurface::~DXSurface()
269  {
270  ImplRenderModuleGuard aGuard( mrRenderModule );
271 
272 #ifdef FAKE_MAX_NUMBER_TEXTURES
273  gNumSurfaces--;
274 #endif
275  }
276 
277 
278  // DXSurface::selectTexture
279 
280 
281  bool DXSurface::selectTexture()
282  {
283  ImplRenderModuleGuard aGuard( mrRenderModule );
284  mrRenderModule.flushVertexCache();
285  COMReference<IDirect3DDevice9> pDevice(mrRenderModule.getDevice());
286 
287  if( FAILED(pDevice->SetTexture(0,mpTexture.get())) )
288  return false;
289 
290  return true;
291  }
292 
293 
294  // DXSurface::isValid
295 
296 
297  bool DXSurface::isValid()
298  {
299  ImplRenderModuleGuard aGuard( mrRenderModule );
300 
301  if(!(mpTexture.is()))
302  return false;
303  return true;
304  }
305 
306 
307  // DXSurface::update
308 
309 
310  bool DXSurface::update( const ::basegfx::B2IPoint& rDestPos,
311  const ::basegfx::B2IRange& rSourceRect,
312  ::canvas::IColorBuffer& rSource )
313  {
314  ImplRenderModuleGuard aGuard( mrRenderModule );
315 
316  // can't update if surface is not valid, that means
317  // either not existent nor restored...
318  if(!(isValid()))
319  return false;
320 
321  D3DLOCKED_RECT aLockedRect;
322  RECT rect;
323  rect.left = std::max(sal_Int32(0),rDestPos.getX());
324  rect.top = std::max(sal_Int32(0),rDestPos.getY());
325  // to avoid interpolation artifacts from other textures,
326  // the surface manager allocates one pixel gap between
327  // them. Clear that to transparent.
328  rect.right = std::min(maSize.getX(),
329  rect.left + sal_Int32(rSourceRect.getWidth()+1));
330  rect.bottom = std::min(maSize.getY(),
331  rect.top + sal_Int32(rSourceRect.getHeight()+1));
332  const bool bClearRightColumn( rect.right < maSize.getX() );
333  const bool bClearBottomRow( rect.bottom < maSize.getY() );
334 
335  if(SUCCEEDED(mpTexture->LockRect(0,&aLockedRect,&rect,D3DLOCK_NOSYSLOCK)))
336  {
337  if(sal_uInt8* pImage = rSource.lock())
338  {
339  switch( rSource.getFormat() )
340  {
341  case ::canvas::IColorBuffer::Format::A8R8G8B8:
342  {
343  const std::size_t nSourceBytesPerPixel(4);
344  const std::size_t nSourcePitchInBytes(rSource.getStride());
345  pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
346  pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
347 
348  // calculate the destination memory address
349  sal_uInt8 *pDst = static_cast<sal_uInt8*>(aLockedRect.pBits);
350 
351  const sal_uInt32 nNumBytesToCopy(
352  static_cast<sal_uInt32>(
353  rSourceRect.getWidth())*
354  nSourceBytesPerPixel);
355  const sal_uInt64 nNumLines(rSourceRect.getHeight());
356 
357  for(sal_uInt64 i=0; i<nNumLines; ++i)
358  {
359  memcpy(pDst,pImage,nNumBytesToCopy);
360 
361  if( bClearRightColumn )
362  {
363  // to avoid interpolation artifacts
364  // from other textures, the surface
365  // manager allocates one pixel gap
366  // between them. Clear that to
367  // transparent.
368  pDst[nNumBytesToCopy] =
369  pDst[nNumBytesToCopy+1] =
370  pDst[nNumBytesToCopy+2] =
371  pDst[nNumBytesToCopy+3] = 0x00;
372  }
373  pDst += aLockedRect.Pitch;
374  pImage += nSourcePitchInBytes;
375  }
376 
377  if( bClearBottomRow )
378  memset(pDst, 0, nNumBytesToCopy+4);
379  }
380  break;
381 
382  case ::canvas::IColorBuffer::Format::X8R8G8B8:
383  {
384  const std::size_t nSourceBytesPerPixel(4);
385  const std::size_t nSourcePitchInBytes(rSource.getStride());
386  pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
387  pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
388 
389  // calculate the destination memory address
390  sal_uInt8 *pDst = static_cast<sal_uInt8*>(aLockedRect.pBits);
391 
392  const sal_Int32 nNumLines(
393  sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
394  const sal_Int32 nNumColumns(
395  sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
396  for(sal_Int32 i=0; i<nNumLines; ++i)
397  {
398  sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage);
399  sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst);
400  for(sal_Int32 j=0; j<nNumColumns; ++j)
401  pDst32[j] = 0xFF000000 | pSrc32[j];
402 
403  if( bClearRightColumn )
404  pDst32[nNumColumns] = 0xFF000000;
405 
406  pDst += aLockedRect.Pitch;
407  pImage += nSourcePitchInBytes;
408  }
409 
410  if( bClearBottomRow )
411  memset(pDst, 0, 4*(nNumColumns+1));
412  }
413  break;
414 
415  default:
417  "DXSurface::update(): Unknown/unimplemented buffer format" );
418  break;
419  }
420 
421  rSource.unlock();
422  }
423 
424  return SUCCEEDED(mpTexture->UnlockRect(0));
425  }
426 
427  return true;
428  }
429 
430 
431  // DXSurface::getSize
432 
433 
434  ::basegfx::B2IVector DXSurface::getSize()
435  {
436  return maSize;
437  }
438 
439  // DXRenderModule::DXRenderModule
440 
441 
442  DXRenderModule::DXRenderModule( const vcl::Window& rWindow ) :
443  mhWnd(nullptr),
444  mpDevice(),
445  mpDirect3D9(),
446  mpSwapChain(),
447  mpVertexBuffer(),
448  mpTexture(),
449  maSize(),
450  maVertexCache(),
451  mnCount(0),
454  mbError( false ),
455  meType( PrimitiveType::Unknown ),
456  maPageSize(),
457  mad3dpp(),
459  maWriteIndex(0),
460  maReadIndex(0)
461  {
462  // TODO(P2): get rid of those fine-grained locking
463  ::osl::MutexGuard aGuard( maMutex );
464 
465  if(!(create(rWindow)))
466  {
467  throw lang::NoSupportException( "Could not create DirectX device!" );
468  }
469 
470  // allocate a single texture surface which can be used later.
471  // we also use this to calibrate the page size.
472  ::basegfx::B2IVector aPageSize(maPageSize);
473  while(true)
474  {
475  mpTexture = std::make_shared<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 
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 allow & denylist 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.isDenylistCurrentDevice() )
641  {
642  aConfigItem.denylistDevice(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::make_shared<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 std::make_shared<DXRenderModule>(rParent);
1210  }
1211 }
1212 
1213 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
int mnBeginSceneCount
Definition: dx_9rm.cxx:179
::basegfx::B2IVector maPageSize
Definition: dx_9rm.cxx:183
std::size_t maNumVertices
Definition: dx_9rm.cxx:195
sal_Int32 nIndex
COMReference< IDirect3DSwapChain9 > mpSwapChain
Definition: dx_9rm.cxx:171
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:182
bool update()
EXTERN_C BOOL BOOL const wchar_t *pProgramPath HRESULT hr
bool mbError
Definition: dx_9rm.cxx:181
std::shared_ptr< IDXRenderModule > IDXRenderModuleSharedPtr
void setY(sal_Int32 fY)
DXRenderModule & mrRenderModule
Definition: dx_9rm.cxx:110
virtual Size GetSizePixel() const
#define max(a, b)
Definition: dx_winstuff.hxx:45
#define MIN_TEXTURE_SIZE
Definition: dx_9rm.cxx:49
void SetParentClipMode(ParentClipMode nMode=ParentClipMode::NONE)
COMReference< IDirect3D9 > mpDirect3D9
Definition: dx_9rm.cxx:170
VclPtr< SystemChildWindow > mpWindow
Definition: dx_9rm.cxx:174
float x
Definition: dx_9rm.cxx:190
Unknown
virtual Format getFormat() const =0
Get format of the color buffer.
bool mbCanUseDynamicTextures
Definition: dx_9rm.cxx:180
#define min(a, b)
Definition: dx_winstuff.hxx:48
COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:169
std::size_t maReadIndex
Definition: dx_9rm.cxx:197
float rhw
Definition: dx_9rm.cxx:190
virtual const SystemEnvData * GetSystemData() const override
HWND mhWnd
Definition: dx_9rm.cxx:168
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:190
vertexCache_t maVertexCache
Definition: dx_9rm.cxx:177
#define TRUE
int i
D3DPRESENT_PARAMETERS mad3dpp
Definition: dx_9rm.cxx:184
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()
float u
Definition: dx_9rm.cxx:192
size
#define VERTEX_BUFFER_SIZE
Definition: dx_9rm.cxx:53
std::size_t mnCount
Definition: dx_9rm.cxx:178
#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)
virtual void setPosSizePixel(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags=PosSizeFlags::All)
#define ENSURE_OR_THROW(c, m)
float v
Definition: dx_9rm.cxx:192
void SetMouseTransparent(bool bTransparent)
unsigned char sal_uInt8
DWORD diffuse
Definition: dx_9rm.cxx:191
float z
Definition: dx_9rm.cxx:190
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:172
void setX(sal_Int32 fX)
::osl::Mutex maMutex
This object represents the DirectX state machine.
Definition: dx_9rm.cxx:166
COMReference< IDirect3DTexture9 > mpTexture
Definition: dx_9rm.cxx:114
std::size_t maWriteIndex
Definition: dx_9rm.cxx:196
std::shared_ptr< osl::Mutex > const & lock()
virtual void unlock() const =0
unlock previous locked buffer
::basegfx::B2IVector maSize
Definition: dx_9rm.cxx:116
void Show(bool bVisible=true, ShowFlags nFlags=ShowFlags::NONE)