/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal // vertex buffer (must be divisible // by 3, as each triangle primitive // has 3 vertices)
usingnamespace ::com::sun::star;
// 'dxcanvas' namespace
namespace dxcanvas
{ namespace
{ class DXRenderModule;
// DXSurface
/** ISurface implementation.
@attention holds the DXRenderModule via non-refcounted reference! This is safe with current state of affairs, since the canvas::PageManager holds surface and render module via shared_ptr (and makes sure all surfaces are deleted before its render module member goes out of scope).
*/ class DXSurface : public canvas::ISurface
{ public:
DXSurface( DXRenderModule& rRenderModule, const ::basegfx::B2ISize& rSize );
~DXSurface() override;
/** This object represents the DirectX state machine. In order to serialize access to DirectX's global state, a global mutex is required.
*/ static ::osl::Mutex maMutex;
// allocate a single texture surface which can be used later. // we also use this to calibrate the page size.
basegfx::B2IVector aPageSize(maPageSize); while(true)
{
mpTexture = std::make_shared<DXSurface>(*this, basegfx::B2ISize(aPageSize.getX(), aPageSize.getY())); if(mpTexture->isValid()) break;
// refrain from releasing the DX9 objects. We're the only // ones holding references to them, and it might be // dangerous to destroy the DX9 device, before all other // objects are dead.
}
// DXRenderModule::create
bool DXRenderModule::create( const vcl::Window& rWindow )
{ // TODO(P2): get rid of those fine-grained locking
::osl::MutexGuard aGuard( maMutex );
// TODO(F2): since we would like to share precious hardware // resources, the direct3d9 object should be global. each new // request for a canvas should only create a new swapchain.
mpDirect3D9 = sal::systools::COMReference<IDirect3D9>(
Direct3DCreate9(D3D_SDK_VERSION), false); if(!mpDirect3D9.is()) returnfalse;
ENSURE_OR_THROW( IsWindow( mhWnd ), "DXRenderModule::create() No valid HWND given." );
// retrieve position and size of the parent window const ::Size &rSizePixel(rWindow.GetSizePixel());
// remember the size of the parent window, since we // need to use this for our child window.
maSize.setWidth(sal_Int32(rSizePixel.Width()));
maSize.setHeight(sal_Int32(rSizePixel.Height()));
// let the child window cover the same size as the parent window.
mpWindow->setPosSizePixel(0, 0, maSize.getWidth(),maSize.getHeight());
// create a device from the direct3d9 object. if(!(createDevice()))
{
mpWindow.disposeAndClear(); returnfalse;
}
// ask direct3d9 about the capabilities of hardware devices on a specific adapter. // here we decide if the underlying hardware of the machine 'is good enough'. // since we only need a tiny little fraction of what could be used, this // is basically a no-op.
D3DCAPS9 aCaps; if(FAILED(mpDirect3D9->GetDeviceCaps(nAdapter,D3DDEVTYPE_HAL,&aCaps))) returnfalse; if(!(aCaps.MaxTextureWidth)) returnfalse; if(!(aCaps.MaxTextureHeight)) returnfalse;
maPageSize = ::basegfx::B2IVector(aCaps.MaxTextureWidth,aCaps.MaxTextureHeight);
bool DXRenderModule::createDevice()
{ // we expect that the caller provides us with a valid HWND
ENSURE_OR_THROW( IsWindow(mhWnd), "DXRenderModule::createDevice() No valid HWND given." );
// we expect that the caller already created the direct3d9 object.
ENSURE_OR_THROW( mpDirect3D9.is(), "DXRenderModule::createDevice() no direct3d?." );
// find the adapter identifier from the window. const UINT aAdapter(getAdapterFromWindow()); if(aAdapter == static_cast<UINT>(-1)) returnfalse;
// verify that device possibly works if( !verifyDevice(aAdapter) ) returnfalse;
// query the display mode from the selected adapter. // we'll later request the backbuffer format to be same // same as the display format.
D3DDISPLAYMODE d3ddm;
mpDirect3D9->GetAdapterDisplayMode(aAdapter,&d3ddm);
// we need to use D3DSWAPEFFECT_COPY here since the canvas-api has // basically nothing to do with efficient resource handling. it tries // to avoid drawing whenever possible, which is simply not the most // efficient way we could leverage the hardware in this case. it would // be far better to redraw the backbuffer each time we would like to // display the content of the backbuffer, but we need to face reality // here and follow how the canvas was designed.
// Strictly speaking, we don't need a full screen worth of // backbuffer here. We could also scale dynamically with // the current window size, but this will make it // necessary to temporarily have two buffers while copying // from the old to the new one. What's more, at the time // we need a larger buffer, DX might not have sufficient // resources available, and we're then left with too small // a back buffer, and no way of falling back to a // different canvas implementation.
ZeroMemory( &mad3dpp, sizeof(mad3dpp) );
mad3dpp.BackBufferWidth = std::max(maSize.getWidth(), sal_Int32(d3ddm.Width));
mad3dpp.BackBufferHeight = std::max(maSize.getHeight(), sal_Int32(d3ddm.Height));
mad3dpp.BackBufferCount = 1;
mad3dpp.Windowed = TRUE;
mad3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
mad3dpp.BackBufferFormat = d3ddm.Format;
mad3dpp.EnableAutoDepthStencil = FALSE;
mad3dpp.hDeviceWindow = mhWnd;
mad3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
// now create the device, first try hardware vertex processing, // then software vertex processing. if both queries fail, we give up // and indicate failure.
IDirect3DDevice9 *pDevice(nullptr); if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
D3DDEVTYPE_HAL,
mhWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING|
D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
&mad3dpp,
&pDevice))) if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
D3DDEVTYPE_HAL,
mhWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING|
D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
&mad3dpp,
&pDevice))) returnfalse;
// got it, store it in a safe place...
mpDevice = sal::systools::COMReference<IDirect3DDevice9>(pDevice, false);
// After CreateDevice, the first swap chain already exists, so just get it...
IDirect3DSwapChain9 *pSwapChain(nullptr);
pDevice->GetSwapChain(0,&pSwapChain);
mpSwapChain = sal::systools::COMReference<IDirect3DSwapChain9>(pSwapChain, false); if( !mpSwapChain.is() ) returnfalse;
// clear the render target [which is the backbuffer in this case]. // we are forced to do this once, and furthermore right now. // please note that this is only possible since we created the // backbuffer with copy semantics [the content is preserved after // calls to Present()], which is an unnecessarily expensive operation.
LPDIRECT3DSURFACE9 pBackBuffer = nullptr;
mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
mpDevice->SetRenderTarget( 0, pBackBuffer );
mpDevice->Clear(0,nullptr,D3DCLEAR_TARGET,0,1.0f,0);
pBackBuffer->Release();
// please note that D3DFMT_X8R8G8B8 is the only format we're // able to choose here, since GetDC() doesn't support any // other 32bit-format.
IDirect3DSurface9 *pSurface(nullptr); if( FAILED(mpDevice->CreateOffscreenPlainSurface(
rSize.getWidth(),
rSize.getHeight(),
D3DFMT_X8R8G8B8,
D3DPOOL_SYSTEMMEM,
&pSurface,
nullptr)) )
{ throw lang::NoSupportException( "Could not create offscreen surface - out of mem!" );
}
void DXRenderModule::resize( const ::basegfx::B2IRange& rect )
{ // TODO(P2): get rid of those fine-grained locking
::osl::MutexGuard aGuard( maMutex );
if(isDisposed()) return;
// don't do anything if the size didn't change. if(maSize.getWidth() == static_cast<sal_Int32>(rect.getWidth()) &&
maSize.getHeight() == static_cast<sal_Int32>(rect.getHeight())) return;
// TODO(Q2): use numeric cast to prevent overflow
maSize.setWidth(sal_Int32(rect.getWidth()));
maSize.setHeight(sal_Int32(rect.getHeight()));
// clear the render target [which is the backbuffer in this case]. // we are forced to do this once, and furthermore right now. // please note that this is only possible since we created the // backbuffer with copy semantics [the content is preserved after // calls to Present()], which is an unnecessarily expensive operation.
LPDIRECT3DSURFACE9 pBackBuffer = nullptr;
mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
mpDevice->SetRenderTarget( 0, pBackBuffer );
mpDevice->Clear(0,nullptr,D3DCLEAR_TARGET,0,1.0f,0);
pBackBuffer->Release();
}
}
// DXRenderModule::getPageSize
::basegfx::B2IVector DXRenderModule::getPageSize()
{ // TODO(P2): get rid of those fine-grained locking
::osl::MutexGuard aGuard( maMutex ); return maPageSize;
}
// DXRenderModule::createSurface
std::shared_ptr<canvas::ISurface> DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
{ // TODO(P2): get rid of those fine-grained locking
::osl::MutexGuard aGuard( maMutex );
// configure the fixed-function pipeline. // the only 'feature' we need here is to modulate the alpha-channels // from the texture and the interpolated diffuse color. the result // will then be blended with the backbuffer. // fragment color = texture color * diffuse.alpha.
mpDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
// normal combination of object... if( FAILED(mpDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA)) ) return;
// ..and background color if( FAILED(mpDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA)) ) return;
// disable backface culling; this enables us to mirror sprites // by simply reverting the triangles, which, with enabled // culling, would be invisible otherwise if( FAILED(mpDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE)) ) return;
// Check to see if there's space for the current set of // vertices in the buffer. if( maNumVertices - maWriteIndex < nSize )
{
commitVertexCache();
dwLockFlags = D3DLOCK_DISCARD;
maWriteIndex = 0;
maReadIndex = 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.