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>
35#include <vcl/syschild.hxx>
36#include <vcl/sysdata.hxx>
37#include <vcl/window.hxx>
38
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
59using namespace ::com::sun::star;
60
61
62// 'dxcanvas' namespace
63
64
65namespace 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 sal::systools::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 sal::systools::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 sal::systools::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 sal::systools::COMReference<IDirect3DDevice9> mpDevice;
170 sal::systools::COMReference<IDirect3D9> mpDirect3D9;
171 sal::systools::COMReference<IDirect3DSwapChain9> mpSwapChain;
172 sal::systools::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;
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 sal::systools::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 = sal::systools::COMReference<IDirect3DTexture9>(pTexture, false);
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 sal::systools::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(),
448 mpTexture(),
449 maSize(),
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.
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 = sal::systools::COMReference<IDirect3DVertexBuffer9>(pVB, false);
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 = sal::systools::COMReference<IDirect3D9>(
546 Direct3DCreate9(D3D_SDK_VERSION), false);
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
563 mpWindow->EnableEraseBackground( false );
564
565 mpWindow->SetControlForeground();
566 mpWindow->SetControlBackground();
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( 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.
584 mpWindow->setPosSizePixel(0,0,maSize.getX(),maSize.getY());
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 = sal::systools::COMReference<IDirect3DDevice9>(pDevice, false);
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 = sal::systools::COMReference<IDirect3DSwapChain9>(pSwapChain, false);
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 sal::systools::COMReference<IDirect3DSurface9> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
761 {
762 if(isDisposed())
763 return sal::systools::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 sal::systools::COMReference<IDirect3DSurface9>(pSurface, false);
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.clear();
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 = sal::systools::COMReference<IDirect3DVertexBuffer9>(pVB, false);
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
879 mpWindow->setPosSizePixel(0,0,maSize.getX(),maSize.getY());
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.clear();
901
902 IDirect3DSwapChain9 *pSwapChain(nullptr);
903 if(FAILED(mpDevice->CreateAdditionalSwapChain(&mad3dpp,&pSwapChain)))
904 return;
905 mpSwapChain = sal::systools::COMReference<IDirect3DSwapChain9>(pSwapChain, false);
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
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 {
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: */
void disposeAndClear()
void reset(reference_type *pBody)
TYPE getX() const
void setY(TYPE fY)
TYPE getY() const
void setX(TYPE fX)
virtual Size GetSizePixel() const
#define ENSURE_OR_RETURN_FALSE(c, m)
#define ENSURE_OR_THROW(c, m)
#define ENSURE_ARG_OR_THROW(c, m)
#define VERTEX_BUFFER_SIZE
Definition: dx_9rm.cxx:53
sal::systools::COMReference< IDirect3DTexture9 > mpTexture
Definition: dx_9rm.cxx:114
VclPtr< SystemChildWindow > mpWindow
Definition: dx_9rm.cxx:174
::basegfx::B2IVector maSize
Definition: dx_9rm.cxx:116
DXRenderModule & mrRenderModule
Definition: dx_9rm.cxx:110
bool mbCanUseDynamicTextures
Definition: dx_9rm.cxx:180
std::size_t maNumVertices
Definition: dx_9rm.cxx:195
::osl::Mutex maMutex
This object represents the DirectX state machine.
Definition: dx_9rm.cxx:166
sal::systools::COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:169
float v
Definition: dx_9rm.cxx:192
std::size_t mnCount
Definition: dx_9rm.cxx:178
sal::systools::COMReference< IDirect3DSwapChain9 > mpSwapChain
Definition: dx_9rm.cxx:171
float u
Definition: dx_9rm.cxx:192
std::size_t maReadIndex
Definition: dx_9rm.cxx:197
PrimitiveType meType
Definition: dx_9rm.cxx:182
HWND mhWnd
Definition: dx_9rm.cxx:168
D3DPRESENT_PARAMETERS mad3dpp
Definition: dx_9rm.cxx:184
sal::systools::COMReference< IDirect3DVertexBuffer9 > mpVertexBuffer
Definition: dx_9rm.cxx:172
float y
Definition: dx_9rm.cxx:190
float rhw
Definition: dx_9rm.cxx:190
::basegfx::B2IVector maPageSize
Definition: dx_9rm.cxx:183
int mnBeginSceneCount
Definition: dx_9rm.cxx:179
bool mbError
Definition: dx_9rm.cxx:181
float x
Definition: dx_9rm.cxx:190
std::size_t maWriteIndex
Definition: dx_9rm.cxx:196
sal::systools::COMReference< IDirect3D9 > mpDirect3D9
Definition: dx_9rm.cxx:170
DWORD diffuse
Definition: dx_9rm.cxx:191
#define MIN_TEXTURE_SIZE
Definition: dx_9rm.cxx:49
float z
Definition: dx_9rm.cxx:190
vertexCache_t maVertexCache
Definition: dx_9rm.cxx:177
#define max(a, b)
Definition: dx_winstuff.hxx:43
DocumentType eType
#define TRUE
#define FALSE
sal_Int32 nIndex
#define SAL_WARN(area, stream)
std::unique_ptr< sal_Int32[]> pData
size
std::shared_ptr< osl::Mutex > const & lock()
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
std::shared_ptr< IDXRenderModule > IDXRenderModuleSharedPtr
IDXRenderModuleSharedPtr createRenderModule(const vcl::Window &rParent)
Factory method, to create an IRenderModule instance for the given VCL window instance.
Definition: dx_9rm.cxx:1207
Unknown
int i
return hr
Interface for a raw memory pixel container.
virtual void unlock() const =0
unlock previous locked buffer
virtual sal_uInt8 * lock() const =0
Get a pointer to the raw memory bits of the pixel.
virtual sal_uInt32 getStride() const =0
Offset, in bytes, between consecutive scan lines of the bitmap.
virtual Format getFormat() const =0
Get format of the color buffer.
unsigned char sal_uInt8
bool update()