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