f0ae48b684
.. now that we have a default value for that parameter Change-Id: I54d6b0a0a01ce2f2e5168ada0c427424de0477bc
1347 lines
48 KiB
C++
1347 lines
48 KiB
C++
/* -*- 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 .
|
|
*/
|
|
|
|
|
|
#if DIRECTX_VERSION == 0x0900
|
|
|
|
#define MAX_TEXTURE_SIZE (2048)
|
|
#define MIN_TEXTURE_SIZE (32)
|
|
//#define FAKE_MAX_NUMBER_TEXTURES (2)
|
|
//#define FAKE_MAX_TEXTURE_SIZE (4096)
|
|
|
|
#define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
|
|
// vertex buffer (must be divisable
|
|
// by 3, as each triangle primitive
|
|
// has 3 vertices)
|
|
#include <string.h>
|
|
|
|
#include <osl/thread.h>
|
|
#include <osl/time.h>
|
|
|
|
#include <vcl/syschild.hxx>
|
|
#include <vcl/window.hxx>
|
|
|
|
#include <canvas/debug.hxx>
|
|
#include <canvas/verbosetrace.hxx>
|
|
#include <tools/diagnose_ex.h>
|
|
|
|
#include <canvas/elapsedtime.hxx>
|
|
#include <canvas/canvastools.hxx>
|
|
#include <canvas/rendering/icolorbuffer.hxx>
|
|
#include <canvas/rendering/isurface.hxx>
|
|
#include <canvas/rendering/irendermodule.hxx>
|
|
#include <basegfx/numeric/ftools.hxx>
|
|
#include <basegfx/vector/b2dsize.hxx>
|
|
#include <basegfx/vector/b2isize.hxx>
|
|
#include <basegfx/point/b2ipoint.hxx>
|
|
#include <basegfx/range/b2irectangle.hxx>
|
|
#include <boost/scoped_ptr.hpp>
|
|
#include <com/sun/star/lang/NoSupportException.hpp>
|
|
|
|
#include "dx_rendermodule.hxx"
|
|
#include "dx_config.hxx"
|
|
|
|
#undef WB_LEFT
|
|
#undef WB_RIGHT
|
|
|
|
#include "dx_impltools.hxx"
|
|
#include <vcl/sysdata.hxx>
|
|
|
|
#if defined(DX_DEBUG_IMAGES)
|
|
# if OSL_DEBUG_LEVEL > 0
|
|
# include <imdebug.h>
|
|
# undef min
|
|
# undef max
|
|
# endif
|
|
#endif
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
|
|
// 'dxcanvas' namespace
|
|
|
|
|
|
namespace dxcanvas
|
|
{
|
|
namespace
|
|
{
|
|
|
|
// monitorSupport
|
|
|
|
|
|
class monitorSupport
|
|
{
|
|
public:
|
|
|
|
monitorSupport() :
|
|
mhLibrary(LoadLibrary("user32.dll")),
|
|
mpMonitorFromWindow(NULL)
|
|
{
|
|
if(mhLibrary)
|
|
mpMonitorFromWindow = reinterpret_cast<fMonitorFromWindow>(
|
|
GetProcAddress(
|
|
mhLibrary,"MonitorFromWindow"));
|
|
}
|
|
|
|
~monitorSupport()
|
|
{
|
|
if(mhLibrary)
|
|
FreeLibrary(mhLibrary);
|
|
mhLibrary=0;
|
|
}
|
|
|
|
HMONITOR MonitorFromWindow( HWND hwnd )
|
|
{
|
|
// return adapter_default in case something went wrong...
|
|
if(!(mpMonitorFromWindow))
|
|
return HMONITOR(0);
|
|
// MONITOR_DEFAULTTONEAREST
|
|
const DWORD dwFlags(0x00000002);
|
|
return mpMonitorFromWindow(hwnd,dwFlags);
|
|
}
|
|
private:
|
|
|
|
HINSTANCE mhLibrary;
|
|
typedef HMONITOR (WINAPI *fMonitorFromWindow )( HWND hwnd, DWORD dwFlags );
|
|
fMonitorFromWindow mpMonitorFromWindow;
|
|
};
|
|
|
|
monitorSupport aMonitorSupport;
|
|
|
|
|
|
class DXRenderModule;
|
|
|
|
|
|
// DXSurface
|
|
|
|
|
|
/** ISurface implemenation.
|
|
|
|
@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();
|
|
|
|
virtual bool selectTexture();
|
|
virtual bool isValid();
|
|
virtual bool update( const ::basegfx::B2IPoint& rDestPos,
|
|
const ::basegfx::B2IRange& rSourceRect,
|
|
::canvas::IColorBuffer& rSource );
|
|
virtual ::basegfx::B2IVector getSize();
|
|
COMReference<IDirect3DTexture9> getTexture() const;
|
|
|
|
private:
|
|
/// Guard local methods against concurrent acces to RenderModule
|
|
class ImplRenderModuleGuard : private ::boost::noncopyable
|
|
{
|
|
public:
|
|
explicit inline ImplRenderModuleGuard( DXRenderModule& rRenderModule );
|
|
inline ~ImplRenderModuleGuard();
|
|
|
|
private:
|
|
DXRenderModule& mrRenderModule;
|
|
};
|
|
|
|
DXRenderModule& mrRenderModule;
|
|
COMReference<IDirect3DTexture9> mpTexture;
|
|
|
|
::basegfx::B2IVector maSize;
|
|
};
|
|
|
|
|
|
|
|
// DXRenderModule
|
|
|
|
|
|
/// Default implementation of IDXRenderModule
|
|
class DXRenderModule : public IDXRenderModule
|
|
{
|
|
public:
|
|
explicit DXRenderModule( const ::Window& rWindow );
|
|
~DXRenderModule();
|
|
|
|
virtual void lock() const { maMutex.acquire(); }
|
|
virtual void unlock() const { maMutex.release(); }
|
|
|
|
virtual COMReference<IDirect3DSurface9>
|
|
createSystemMemorySurface( const ::basegfx::B2IVector& rSize );
|
|
virtual void disposing();
|
|
virtual HWND getHWND() const { return mhWnd; }
|
|
virtual void screenShot();
|
|
|
|
virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
|
|
const ::basegfx::B2IRectangle& rCurrWindowArea );
|
|
|
|
virtual void resize( const ::basegfx::B2IRange& rect );
|
|
virtual ::basegfx::B2IVector getPageSize();
|
|
virtual ::canvas::ISurfaceSharedPtr createSurface( const ::basegfx::B2IVector& surfaceSize );
|
|
virtual void beginPrimitive( PrimitiveType eType );
|
|
virtual void endPrimitive();
|
|
virtual void pushVertex( const ::canvas::Vertex& vertex );
|
|
virtual bool isError();
|
|
|
|
COMReference<IDirect3DDevice9> getDevice() { return mpDevice; }
|
|
|
|
void flushVertexCache();
|
|
void commitVertexCache();
|
|
|
|
private:
|
|
|
|
bool create( const ::Window& rWindow );
|
|
bool createDevice();
|
|
bool verifyDevice( const UINT nAdapter );
|
|
UINT getAdapterFromWindow();
|
|
|
|
/** 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;
|
|
|
|
HWND mhWnd;
|
|
COMReference<IDirect3DDevice9> mpDevice;
|
|
COMReference<IDirect3D9> mpDirect3D9;
|
|
COMReference<IDirect3DSwapChain9> mpSwapChain;
|
|
COMReference<IDirect3DVertexBuffer9> mpVertexBuffer;
|
|
::canvas::ISurfaceSharedPtr mpTexture;
|
|
::boost::scoped_ptr<SystemChildWindow> mpWindow;
|
|
::basegfx::B2IVector maSize;
|
|
typedef std::vector<canvas::Vertex> vertexCache_t;
|
|
vertexCache_t maVertexCache;
|
|
std::size_t mnCount;
|
|
int mnBeginSceneCount;
|
|
bool mbCanUseDynamicTextures;
|
|
bool mbError;
|
|
PrimitiveType meType;
|
|
::basegfx::B2IVector maPageSize;
|
|
D3DPRESENT_PARAMETERS mad3dpp;
|
|
|
|
inline bool isDisposed() const { return (mhWnd==NULL); }
|
|
|
|
struct dxvertex
|
|
{
|
|
float x,y,z,rhw;
|
|
DWORD diffuse;
|
|
float u,v;
|
|
};
|
|
|
|
std::size_t maNumVertices;
|
|
std::size_t maWriteIndex;
|
|
std::size_t maReadIndex;
|
|
};
|
|
|
|
::osl::Mutex DXRenderModule::maMutex;
|
|
|
|
|
|
// DXSurface::ImplRenderModuleGuard
|
|
|
|
|
|
inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
|
|
DXRenderModule& rRenderModule ) :
|
|
mrRenderModule( rRenderModule )
|
|
{
|
|
mrRenderModule.lock();
|
|
}
|
|
|
|
inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
|
|
{
|
|
mrRenderModule.unlock();
|
|
}
|
|
|
|
#ifdef FAKE_MAX_NUMBER_TEXTURES
|
|
static sal_uInt32 gNumSurfaces = 0;
|
|
#endif
|
|
|
|
void fillRect( sal_uInt32 *pDest,
|
|
sal_uInt32 dwWidth,
|
|
sal_uInt32 dwHeight,
|
|
sal_uInt32 dwPitch,
|
|
sal_uInt32 dwColor )
|
|
{
|
|
for(sal_uInt32 i=0; i<dwWidth; ++i)
|
|
{
|
|
pDest[i]=dwColor;
|
|
pDest[((dwHeight-1)*dwPitch)+i]=dwColor;
|
|
}
|
|
|
|
for(sal_uInt32 j=0; j<dwHeight; ++j)
|
|
{
|
|
pDest[0]=dwColor;
|
|
pDest[dwWidth-1]=dwColor;
|
|
pDest += dwPitch;
|
|
}
|
|
}
|
|
|
|
|
|
// DXSurface::DXSurface
|
|
|
|
|
|
DXSurface::DXSurface( DXRenderModule& rRenderModule,
|
|
const ::basegfx::B2ISize& rSize ) :
|
|
mrRenderModule(rRenderModule),
|
|
mpTexture(NULL),
|
|
maSize()
|
|
{
|
|
ImplRenderModuleGuard aGuard( mrRenderModule );
|
|
|
|
#ifdef FAKE_MAX_NUMBER_TEXTURES
|
|
++gNumSurfaces;
|
|
if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
|
|
return;
|
|
#endif
|
|
|
|
#ifdef FAKE_MAX_TEXTURE_SIZE
|
|
if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE)
|
|
return;
|
|
if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE)
|
|
return;
|
|
#endif
|
|
|
|
ENSURE_ARG_OR_THROW(rSize.getX() > 0 && rSize.getY() > 0,
|
|
"DXSurface::DXSurface(): request for zero-sized surface");
|
|
|
|
COMReference<IDirect3DDevice9> pDevice(rRenderModule.getDevice());
|
|
|
|
IDirect3DTexture9 *pTexture(NULL);
|
|
if(FAILED(pDevice->CreateTexture(
|
|
rSize.getX(),
|
|
rSize.getY(),
|
|
1,0,D3DFMT_A8R8G8B8,
|
|
D3DPOOL_MANAGED,
|
|
&pTexture,NULL)))
|
|
return;
|
|
|
|
mpTexture=COMReference<IDirect3DTexture9>(pTexture);
|
|
maSize = rSize;
|
|
}
|
|
|
|
|
|
// DXSurface::~DXSurface
|
|
|
|
|
|
DXSurface::~DXSurface()
|
|
{
|
|
ImplRenderModuleGuard aGuard( mrRenderModule );
|
|
|
|
#ifdef FAKE_MAX_NUMBER_TEXTURES
|
|
gNumSurfaces--;
|
|
#endif
|
|
}
|
|
|
|
|
|
// DXSurface::selectTexture
|
|
|
|
|
|
bool DXSurface::selectTexture()
|
|
{
|
|
ImplRenderModuleGuard aGuard( mrRenderModule );
|
|
mrRenderModule.flushVertexCache();
|
|
COMReference<IDirect3DDevice9> pDevice(mrRenderModule.getDevice());
|
|
|
|
if( FAILED(pDevice->SetTexture(0,mpTexture.get())) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// DXSurface::isValid
|
|
|
|
|
|
bool DXSurface::isValid()
|
|
{
|
|
ImplRenderModuleGuard aGuard( mrRenderModule );
|
|
|
|
if(!(mpTexture.is()))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
// DXSurface::update
|
|
|
|
|
|
bool DXSurface::update( const ::basegfx::B2IPoint& rDestPos,
|
|
const ::basegfx::B2IRange& rSourceRect,
|
|
::canvas::IColorBuffer& rSource )
|
|
{
|
|
ImplRenderModuleGuard aGuard( mrRenderModule );
|
|
|
|
// can't update if surface is not valid, that means
|
|
// either not existent nor restored...
|
|
if(!(isValid()))
|
|
return false;
|
|
|
|
D3DLOCKED_RECT aLockedRect;
|
|
RECT rect;
|
|
rect.left = std::max(sal_Int32(0),rDestPos.getX());
|
|
rect.top = std::max(sal_Int32(0),rDestPos.getY());
|
|
// to avoid interpolation artifacts from other textures,
|
|
// the surface manager allocates one pixel gap between
|
|
// them. Clear that to transparent.
|
|
rect.right = std::min(maSize.getX(),
|
|
rect.left + sal_Int32(rSourceRect.getWidth()+1));
|
|
rect.bottom = std::min(maSize.getY(),
|
|
rect.top + sal_Int32(rSourceRect.getHeight()+1));
|
|
const bool bClearRightColumn( rect.right < maSize.getX() );
|
|
const bool bClearBottomRow( rect.bottom < maSize.getY() );
|
|
|
|
if(SUCCEEDED(mpTexture->LockRect(0,&aLockedRect,&rect,D3DLOCK_NOSYSLOCK)))
|
|
{
|
|
if(sal_uInt8* pImage = rSource.lock())
|
|
{
|
|
switch( rSource.getFormat() )
|
|
{
|
|
case ::canvas::IColorBuffer::FMT_A8R8G8B8:
|
|
{
|
|
const std::size_t nSourceBytesPerPixel(4);
|
|
const std::size_t nSourcePitchInBytes(rSource.getStride());
|
|
pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
|
|
pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
|
|
|
|
// calculate the destination memory address
|
|
sal_uInt8 *pDst = (sal_uInt8*)aLockedRect.pBits;
|
|
|
|
const sal_uInt32 nNumBytesToCopy(
|
|
static_cast<sal_uInt32>(
|
|
rSourceRect.getWidth())*
|
|
nSourceBytesPerPixel);
|
|
const sal_uInt64 nNumLines(rSourceRect.getHeight());
|
|
|
|
for(sal_uInt32 i=0; i<nNumLines; ++i)
|
|
{
|
|
memcpy(pDst,pImage,nNumBytesToCopy);
|
|
|
|
if( bClearRightColumn )
|
|
{
|
|
// to avoid interpolation artifacts
|
|
// from other textures, the surface
|
|
// manager allocates one pixel gap
|
|
// between them. Clear that to
|
|
// transparent.
|
|
pDst[nNumBytesToCopy] =
|
|
pDst[nNumBytesToCopy+1] =
|
|
pDst[nNumBytesToCopy+2] =
|
|
pDst[nNumBytesToCopy+3] = 0x00;
|
|
}
|
|
pDst += aLockedRect.Pitch;
|
|
pImage += nSourcePitchInBytes;
|
|
}
|
|
|
|
if( bClearBottomRow )
|
|
memset(pDst, 0, nNumBytesToCopy+4);
|
|
}
|
|
break;
|
|
|
|
case ::canvas::IColorBuffer::FMT_R8G8B8:
|
|
{
|
|
const std::size_t nSourceBytesPerPixel(3);
|
|
const std::size_t nSourcePitchInBytes(rSource.getStride());
|
|
pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
|
|
pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
|
|
|
|
// calculate the destination memory address
|
|
sal_uInt8 *pDst = (sal_uInt8*)aLockedRect.pBits;
|
|
|
|
const sal_Int32 nNumColumns(
|
|
sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
|
|
const sal_Int32 nNumLines(
|
|
sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
|
|
for(sal_Int32 i=0; i<nNumLines; ++i)
|
|
{
|
|
sal_uInt32 *pDstScanline = reinterpret_cast<sal_uInt32 *>(pDst);
|
|
sal_uInt8 *pSrcScanline = reinterpret_cast<sal_uInt8 *>(pImage);
|
|
|
|
for(sal_Int32 x=0; x<nNumColumns; ++x)
|
|
{
|
|
sal_uInt32 color(0xFF000000);
|
|
color |= pSrcScanline[2]<<16;
|
|
color |= pSrcScanline[1]<<8;
|
|
color |= pSrcScanline[0];
|
|
pSrcScanline += 3;
|
|
*pDstScanline++ = color;
|
|
}
|
|
if( bClearRightColumn )
|
|
*pDstScanline++ = 0xFF000000;
|
|
|
|
pDst += aLockedRect.Pitch;
|
|
pImage += nSourcePitchInBytes;
|
|
}
|
|
|
|
if( bClearBottomRow )
|
|
memset(pDst, 0, 4*(nNumColumns+1));
|
|
}
|
|
break;
|
|
|
|
case ::canvas::IColorBuffer::FMT_X8R8G8B8:
|
|
{
|
|
const std::size_t nSourceBytesPerPixel(4);
|
|
const std::size_t nSourcePitchInBytes(rSource.getStride());
|
|
pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
|
|
pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
|
|
|
|
// calculate the destination memory address
|
|
sal_uInt8 *pDst = (sal_uInt8*)aLockedRect.pBits;
|
|
|
|
const sal_Int32 nNumLines(
|
|
sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
|
|
const sal_Int32 nNumColumns(
|
|
sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
|
|
for(sal_Int32 i=0; i<nNumLines; ++i)
|
|
{
|
|
sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage);
|
|
sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst);
|
|
for(sal_Int32 j=0; j<nNumColumns; ++j)
|
|
pDst32[j] = 0xFF000000 | pSrc32[j];
|
|
|
|
if( bClearRightColumn )
|
|
pDst32[nNumColumns] = 0xFF000000;
|
|
|
|
pDst += aLockedRect.Pitch;
|
|
pImage += nSourcePitchInBytes;
|
|
}
|
|
|
|
if( bClearBottomRow )
|
|
memset(pDst, 0, 4*(nNumColumns+1));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ENSURE_OR_RETURN_FALSE(false,
|
|
"DXSurface::update(): Unknown/unimplemented buffer format" );
|
|
break;
|
|
}
|
|
|
|
rSource.unlock();
|
|
}
|
|
|
|
return SUCCEEDED(mpTexture->UnlockRect(0));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// DXSurface::getSize
|
|
|
|
|
|
::basegfx::B2IVector DXSurface::getSize()
|
|
{
|
|
return maSize;
|
|
}
|
|
|
|
COMReference<IDirect3DTexture9> DXSurface::getTexture() const
|
|
{
|
|
return mpTexture;
|
|
}
|
|
|
|
|
|
// DXRenderModule::DXRenderModule
|
|
|
|
|
|
DXRenderModule::DXRenderModule( const ::Window& rWindow ) :
|
|
mhWnd(0),
|
|
mpDevice(),
|
|
mpDirect3D9(),
|
|
mpSwapChain(),
|
|
mpVertexBuffer(),
|
|
mpTexture(),
|
|
maSize(),
|
|
maVertexCache(),
|
|
mnCount(0),
|
|
mnBeginSceneCount(0),
|
|
mbCanUseDynamicTextures(false),
|
|
mbError( false ),
|
|
meType( PRIMITIVE_TYPE_UNKNOWN ),
|
|
maPageSize(),
|
|
mad3dpp(),
|
|
maNumVertices( VERTEX_BUFFER_SIZE ),
|
|
maWriteIndex(0),
|
|
maReadIndex(0)
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
if(!(create(rWindow)))
|
|
{
|
|
throw lang::NoSupportException( "Could not create DirectX device!" );
|
|
}
|
|
|
|
// 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 = ::canvas::ISurfaceSharedPtr(
|
|
new DXSurface(*this,aPageSize));
|
|
if(mpTexture->isValid())
|
|
break;
|
|
|
|
aPageSize.setX(aPageSize.getX()>>1);
|
|
aPageSize.setY(aPageSize.getY()>>1);
|
|
if((aPageSize.getX() < MIN_TEXTURE_SIZE) ||
|
|
(aPageSize.getY() < MIN_TEXTURE_SIZE))
|
|
{
|
|
throw lang::NoSupportException(
|
|
"Could not create DirectX device - insufficient texture space!" );
|
|
}
|
|
}
|
|
maPageSize=aPageSize;
|
|
|
|
IDirect3DVertexBuffer9 *pVB(NULL);
|
|
DWORD aFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1);
|
|
if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
|
|
D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
|
|
aFVF,
|
|
D3DPOOL_DEFAULT,
|
|
&pVB,
|
|
NULL)) )
|
|
{
|
|
throw lang::NoSupportException(
|
|
"Could not create DirectX device - out of memory!" );
|
|
}
|
|
|
|
mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
|
|
}
|
|
|
|
|
|
// DXRenderModule::~DXRenderModule
|
|
|
|
|
|
DXRenderModule::~DXRenderModule()
|
|
{
|
|
disposing();
|
|
}
|
|
|
|
|
|
// DXRenderModule::disposing
|
|
|
|
|
|
void DXRenderModule::disposing()
|
|
{
|
|
if(!(mhWnd))
|
|
return;
|
|
|
|
mpTexture.reset();
|
|
mpWindow.reset();
|
|
mhWnd=NULL;
|
|
|
|
// 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 ::Window& rWindow )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
maVertexCache.reserve(1024);
|
|
|
|
mpWindow.reset(
|
|
new SystemChildWindow(
|
|
const_cast<Window *>(&rWindow), 0) );
|
|
|
|
// system child window must not receive mouse events
|
|
mpWindow->SetMouseTransparent( TRUE );
|
|
|
|
// parent should receive paint messages as well
|
|
// [PARENTCLIPMODE_NOCLIP], the argument is here
|
|
// passed as plain numeric value since the stupid
|
|
// define utilizes a USHORT cast.
|
|
mpWindow->SetParentClipMode(0x0002);
|
|
|
|
// the system child window must not clear its background
|
|
mpWindow->EnableEraseBackground( sal_False );
|
|
|
|
mpWindow->SetControlForeground();
|
|
mpWindow->SetControlBackground();
|
|
mpWindow->EnablePaint(sal_False);
|
|
|
|
const SystemEnvData *pData = mpWindow->GetSystemData();
|
|
const HWND hwnd(reinterpret_cast<HWND>(pData->hWnd));
|
|
mhWnd = const_cast<HWND>(hwnd);
|
|
|
|
ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND>(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.setX(static_cast<sal_Int32>(rSizePixel.Width()));
|
|
maSize.setY(static_cast<sal_Int32>(rSizePixel.Height()));
|
|
|
|
// let the child window cover the same size as the parent window.
|
|
mpWindow->setPosSizePixel(0,0,maSize.getX(),maSize.getY());
|
|
|
|
// 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 = COMReference<IDirect3D9>(
|
|
Direct3DCreate9(D3D_SDK_VERSION));
|
|
if(!mpDirect3D9.is())
|
|
return false;
|
|
|
|
// create a device from the direct3d9 object.
|
|
if(!(createDevice()))
|
|
return false;
|
|
|
|
mpWindow->Show();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// DXRenderModule::verifyDevice
|
|
|
|
|
|
bool DXRenderModule::verifyDevice( const UINT nAdapter )
|
|
{
|
|
ENSURE_OR_THROW( mpDirect3D9.is(),
|
|
"DXRenderModule::verifyDevice() No valid device." );
|
|
|
|
// 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)))
|
|
return false;
|
|
if(!(aCaps.MaxTextureWidth))
|
|
return false;
|
|
if(!(aCaps.MaxTextureHeight))
|
|
return false;
|
|
maPageSize = ::basegfx::B2IVector(aCaps.MaxTextureWidth,aCaps.MaxTextureHeight);
|
|
|
|
// check device against white & blacklist entries
|
|
D3DADAPTER_IDENTIFIER9 aIdent;
|
|
if(FAILED(mpDirect3D9->GetAdapterIdentifier(nAdapter,0,&aIdent)))
|
|
return false;
|
|
|
|
DXCanvasItem aConfigItem;
|
|
DXCanvasItem::DeviceInfo aInfo;
|
|
aInfo.nVendorId = aIdent.VendorId;
|
|
aInfo.nDeviceId = aIdent.DeviceId;
|
|
aInfo.nDeviceSubSysId = aIdent.SubSysId;
|
|
aInfo.nDeviceRevision = aIdent.Revision;
|
|
|
|
aInfo.nDriverId = HIWORD(aIdent.DriverVersion.HighPart);
|
|
aInfo.nDriverVersion = LOWORD(aIdent.DriverVersion.HighPart);
|
|
aInfo.nDriverSubVersion = HIWORD(aIdent.DriverVersion.LowPart);
|
|
aInfo.nDriverBuildId = LOWORD(aIdent.DriverVersion.LowPart);
|
|
|
|
if( !aConfigItem.isDeviceUsable(aInfo) )
|
|
return false;
|
|
|
|
if( aConfigItem.isBlacklistCurrentDevice() )
|
|
{
|
|
aConfigItem.blacklistDevice(aInfo);
|
|
return false;
|
|
}
|
|
|
|
aConfigItem.adaptMaxTextureSize(maPageSize);
|
|
|
|
mbCanUseDynamicTextures = (aCaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// DXRenderModule::createDevice
|
|
|
|
|
|
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))
|
|
return false;
|
|
|
|
// verify that device possibly works
|
|
if( !verifyDevice(aAdapter) )
|
|
return false;
|
|
|
|
// 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 whenevery 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(sal_Int32(maSize.getX()),
|
|
sal_Int32(d3ddm.Width));
|
|
mad3dpp.BackBufferHeight = std::max(sal_Int32(maSize.getY()),
|
|
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(NULL);
|
|
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)))
|
|
return false;
|
|
|
|
// got it, store it in a safe place...
|
|
mpDevice=COMReference<IDirect3DDevice9>(pDevice);
|
|
|
|
// After CreateDevice, the first swap chain already exists, so just get it...
|
|
IDirect3DSwapChain9 *pSwapChain(NULL);
|
|
pDevice->GetSwapChain(0,&pSwapChain);
|
|
mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
|
|
if( !mpSwapChain.is() )
|
|
return false;
|
|
|
|
// 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 = NULL;
|
|
mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
|
|
mpDevice->SetRenderTarget( 0, pBackBuffer );
|
|
mpDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0L);
|
|
pBackBuffer->Release();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// DXRenderModule::createSystemMemorySurface
|
|
|
|
|
|
COMReference<IDirect3DSurface9> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
|
|
{
|
|
if(isDisposed())
|
|
return COMReference<IDirect3DSurface9>(NULL);
|
|
|
|
// 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(NULL);
|
|
if( FAILED(mpDevice->CreateOffscreenPlainSurface(
|
|
rSize.getX(),
|
|
rSize.getY(),
|
|
D3DFMT_X8R8G8B8,
|
|
D3DPOOL_SYSTEMMEM,
|
|
&pSurface,
|
|
NULL)) )
|
|
{
|
|
throw lang::NoSupportException(
|
|
"Could not create offscreen surface - out of mem!" );
|
|
}
|
|
|
|
return COMReference<IDirect3DSurface9>(pSurface);
|
|
}
|
|
|
|
|
|
// DXRenderModule::flip
|
|
|
|
|
|
bool DXRenderModule::flip( const ::basegfx::B2IRectangle& rUpdateArea,
|
|
const ::basegfx::B2IRectangle& /*rCurrWindowArea*/ )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
if(isDisposed() || !mpSwapChain.is())
|
|
return false;
|
|
|
|
flushVertexCache();
|
|
|
|
// TODO(P2): Might be faster to actually pass update area here
|
|
RECT aRect =
|
|
{
|
|
rUpdateArea.getMinX(),
|
|
rUpdateArea.getMinY(),
|
|
rUpdateArea.getMaxX(),
|
|
rUpdateArea.getMaxY()
|
|
};
|
|
HRESULT hr(mpSwapChain->Present(&aRect,&aRect,NULL,NULL,0));
|
|
if(FAILED(hr))
|
|
{
|
|
if(hr != D3DERR_DEVICELOST)
|
|
return false;
|
|
|
|
// interestingly enough, sometimes the Reset() below
|
|
// *still* causes DeviceLost errors. So, cycle until
|
|
// DX was kind enough to really reset the device...
|
|
do
|
|
{
|
|
mpVertexBuffer.reset();
|
|
hr = mpDevice->Reset(&mad3dpp);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
IDirect3DVertexBuffer9 *pVB(NULL);
|
|
DWORD aFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1);
|
|
if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
|
|
D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
|
|
aFVF,
|
|
D3DPOOL_DEFAULT,
|
|
&pVB,
|
|
NULL)) )
|
|
{
|
|
throw lang::NoSupportException(
|
|
"Could not create DirectX device - out of memory!" );
|
|
}
|
|
mpVertexBuffer=COMReference<IDirect3DVertexBuffer9>(pVB);
|
|
|
|
// retry after the restore
|
|
if(SUCCEEDED(mpSwapChain->Present(&aRect,&aRect,NULL,NULL,0)))
|
|
return true;
|
|
}
|
|
|
|
TimeValue aTimeout;
|
|
aTimeout.Seconds=1;
|
|
aTimeout.Nanosec=0;
|
|
osl_waitThread(&aTimeout);
|
|
}
|
|
while(hr == D3DERR_DEVICELOST);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// DXRenderModule::screenShot
|
|
|
|
|
|
void DXRenderModule::screenShot()
|
|
{
|
|
}
|
|
|
|
|
|
// DXRenderModule::resize
|
|
|
|
|
|
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.getX() == static_cast<sal_Int32>(rect.getWidth()) &&
|
|
maSize.getY() == static_cast<sal_Int32>(rect.getHeight()))
|
|
return;
|
|
|
|
// TODO(Q2): use numeric cast to prevent overflow
|
|
maSize.setX(static_cast<sal_Int32>(rect.getWidth()));
|
|
maSize.setY(static_cast<sal_Int32>(rect.getHeight()));
|
|
|
|
mpWindow->setPosSizePixel(0,0,maSize.getX(),maSize.getY());
|
|
|
|
// resize back buffer, if necessary
|
|
|
|
|
|
// don't attempt to create anything if the
|
|
// requested size is NULL.
|
|
if(!(maSize.getX()))
|
|
return;
|
|
if(!(maSize.getY()))
|
|
return;
|
|
|
|
// backbuffer too small (might happen, if window is
|
|
// maximized across multiple monitors)
|
|
if( sal_Int32(mad3dpp.BackBufferWidth) < maSize.getX() ||
|
|
sal_Int32(mad3dpp.BackBufferHeight) < maSize.getY() )
|
|
{
|
|
mad3dpp.BackBufferWidth = maSize.getX();
|
|
mad3dpp.BackBufferHeight = maSize.getY();
|
|
|
|
// clear before, save resources
|
|
mpSwapChain.reset();
|
|
|
|
IDirect3DSwapChain9 *pSwapChain(NULL);
|
|
if(FAILED(mpDevice->CreateAdditionalSwapChain(&mad3dpp,&pSwapChain)))
|
|
return;
|
|
mpSwapChain=COMReference<IDirect3DSwapChain9>(pSwapChain);
|
|
|
|
// 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 = NULL;
|
|
mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
|
|
mpDevice->SetRenderTarget( 0, pBackBuffer );
|
|
mpDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0L);
|
|
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
|
|
|
|
|
|
::canvas::ISurfaceSharedPtr DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
if(isDisposed())
|
|
return ::canvas::ISurfaceSharedPtr();
|
|
|
|
const ::basegfx::B2IVector& rPageSize( getPageSize() );
|
|
::basegfx::B2ISize aSize(surfaceSize);
|
|
if(!(aSize.getX()))
|
|
aSize.setX(rPageSize.getX());
|
|
if(!(aSize.getY()))
|
|
aSize.setY(rPageSize.getY());
|
|
|
|
if(mpTexture.use_count() == 1)
|
|
return mpTexture;
|
|
|
|
return ::canvas::ISurfaceSharedPtr( new DXSurface(*this,aSize) );
|
|
}
|
|
|
|
|
|
// DXRenderModule::beginPrimitive
|
|
|
|
|
|
void DXRenderModule::beginPrimitive( PrimitiveType eType )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
if(isDisposed())
|
|
return;
|
|
|
|
ENSURE_OR_THROW( !mnBeginSceneCount,
|
|
"DXRenderModule::beginPrimitive(): nested call" );
|
|
|
|
++mnBeginSceneCount;
|
|
meType=eType;
|
|
mnCount=0;
|
|
}
|
|
|
|
|
|
// DXRenderModule::endPrimitive
|
|
|
|
|
|
void DXRenderModule::endPrimitive()
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
if(isDisposed())
|
|
return;
|
|
|
|
--mnBeginSceneCount;
|
|
meType=PRIMITIVE_TYPE_UNKNOWN;
|
|
mnCount=0;
|
|
}
|
|
|
|
|
|
// DXRenderModule::pushVertex
|
|
|
|
|
|
void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
if(isDisposed())
|
|
return;
|
|
|
|
switch(meType)
|
|
{
|
|
case PRIMITIVE_TYPE_TRIANGLE:
|
|
{
|
|
maVertexCache.push_back(vertex);
|
|
++mnCount;
|
|
mnCount &= 3;
|
|
break;
|
|
}
|
|
|
|
case PRIMITIVE_TYPE_QUAD:
|
|
{
|
|
if(mnCount == 3)
|
|
{
|
|
const std::size_t size(maVertexCache.size());
|
|
::canvas::Vertex v0(maVertexCache[size-1]);
|
|
::canvas::Vertex v2(maVertexCache[size-3]);
|
|
maVertexCache.push_back(v0);
|
|
maVertexCache.push_back(vertex);
|
|
maVertexCache.push_back(v2);
|
|
mnCount=0;
|
|
}
|
|
else
|
|
{
|
|
maVertexCache.push_back(vertex);
|
|
++mnCount;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
OSL_FAIL("DXRenderModule::pushVertex(): unexpected primitive type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// DXRenderModule::isError
|
|
|
|
|
|
bool DXRenderModule::isError()
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
return mbError;
|
|
}
|
|
|
|
|
|
// DXRenderModule::getAdapterFromWindow
|
|
|
|
|
|
UINT DXRenderModule::getAdapterFromWindow()
|
|
{
|
|
HMONITOR hMonitor(aMonitorSupport.MonitorFromWindow(mhWnd));
|
|
UINT aAdapterCount(mpDirect3D9->GetAdapterCount());
|
|
for(UINT i=0; i<aAdapterCount; ++i)
|
|
if(hMonitor == mpDirect3D9->GetAdapterMonitor(i))
|
|
return i;
|
|
return static_cast<UINT>(-1);
|
|
}
|
|
|
|
|
|
// DXRenderModule::commitVertexCache
|
|
|
|
|
|
void DXRenderModule::commitVertexCache()
|
|
{
|
|
if(maReadIndex != maWriteIndex)
|
|
{
|
|
const std::size_t nVertexStride = sizeof(dxvertex);
|
|
const unsigned int nNumVertices = maWriteIndex-maReadIndex;
|
|
const unsigned int nNumPrimitives = nNumVertices / 3;
|
|
|
|
if(FAILED(mpDevice->SetStreamSource(0,mpVertexBuffer.get(),0,nVertexStride)))
|
|
return;
|
|
|
|
if(FAILED(mpDevice->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)))
|
|
return;
|
|
|
|
if(FAILED(mpDevice->BeginScene()))
|
|
return;
|
|
|
|
mbError |= FAILED(mpDevice->DrawPrimitive(D3DPT_TRIANGLELIST,maReadIndex,nNumPrimitives));
|
|
mbError |= FAILED(mpDevice->EndScene());
|
|
|
|
maReadIndex += nNumVertices;
|
|
}
|
|
}
|
|
|
|
|
|
// DXRenderModule::flushVertexCache
|
|
|
|
|
|
void DXRenderModule::flushVertexCache()
|
|
{
|
|
if(!(maVertexCache.size()))
|
|
return;
|
|
|
|
mbError=true;
|
|
|
|
if( FAILED(mpDevice->SetRenderState(D3DRS_LIGHTING,FALSE)))
|
|
return;
|
|
|
|
// enable texture alpha blending
|
|
if( FAILED(mpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE)))
|
|
return;
|
|
|
|
mpDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
|
|
mpDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
|
|
mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSU ,D3DTADDRESS_CLAMP );
|
|
mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSV ,D3DTADDRESS_CLAMP );
|
|
|
|
// 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;
|
|
|
|
mbError=false;
|
|
|
|
std::size_t nSize(maVertexCache.size());
|
|
const std::size_t nVertexStride = sizeof(dxvertex);
|
|
|
|
const ::basegfx::B2IVector aPageSize(getPageSize());
|
|
const float nHalfPixelSizeX(0.5f/aPageSize.getX());
|
|
const float nHalfPixelSizeY(0.5f/aPageSize.getY());
|
|
vertexCache_t::const_iterator it(maVertexCache.begin());
|
|
|
|
while( nSize )
|
|
{
|
|
DWORD dwLockFlags(D3DLOCK_NOOVERWRITE);
|
|
|
|
// 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;
|
|
}
|
|
|
|
dxvertex *vertices(NULL);
|
|
const std::size_t nNumVertices(
|
|
std::min(maNumVertices - maWriteIndex,
|
|
nSize));
|
|
if(FAILED(mpVertexBuffer->Lock(maWriteIndex*nVertexStride,
|
|
nNumVertices*nVertexStride,
|
|
(void **)&vertices,
|
|
dwLockFlags)))
|
|
return;
|
|
|
|
std::size_t nIndex(0);
|
|
while( nIndex < nNumVertices )
|
|
{
|
|
dxvertex &dest = vertices[nIndex++];
|
|
dest.x=it->x;
|
|
dest.y=it->y;
|
|
dest.z=it->z;
|
|
dest.rhw=1;
|
|
const sal_uInt32 alpha(static_cast<sal_uInt32>(it->a*255.0f));
|
|
dest.diffuse=D3DCOLOR_ARGB(alpha,255,255,255);
|
|
dest.u=static_cast<float>(it->u + nHalfPixelSizeX);
|
|
dest.v=static_cast<float>(it->v + nHalfPixelSizeY);
|
|
++it;
|
|
}
|
|
|
|
mpVertexBuffer->Unlock();
|
|
|
|
// Advance to the next position in the vertex buffer.
|
|
maWriteIndex += nNumVertices;
|
|
nSize -= nNumVertices;
|
|
|
|
commitVertexCache();
|
|
}
|
|
|
|
maVertexCache.clear();
|
|
}
|
|
}
|
|
|
|
|
|
// createRenderModule
|
|
|
|
|
|
IDXRenderModuleSharedPtr createRenderModule( const ::Window& rParent )
|
|
{
|
|
return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) );
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|