4fdd55226d
Conflicts: comphelper/source/misc/servicedecl.cxx i18npool/source/breakiterator/breakiteratorImpl.cxx l10ntools/scripts/localize.pl svl/source/items/itemset.cxx svl/source/memtools/svarray.cxx svl/source/numbers/zformat.cxx svtools/source/brwbox/brwbox1.cxx tools/source/stream/strmwnt.cxx vcl/inc/vcl/graphite_adaptors.hxx vcl/inc/vcl/graphite_layout.hxx vcl/inc/vcl/graphite_serverfont.hxx vcl/source/control/imgctrl.cxx vcl/source/gdi/outdev.cxx vcl/source/gdi/outdev3.cxx vcl/source/glyphs/gcach_ftyp.cxx vcl/source/glyphs/graphite_adaptors.cxx vcl/source/glyphs/graphite_layout.cxx vcl/source/window/winproc.cxx vcl/unx/source/fontmanager/fontconfig.cxx
2285 lines
94 KiB
C++
2285 lines
94 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2000, 2010 Oracle and/or its affiliates.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_canvas.hxx"
|
|
|
|
#if DIRECTX_VERSION < 0x0900
|
|
|
|
// Nvidia GeForce Go 6800 crashes with a bluescreen if we take the
|
|
// maximum texture size, which would be twice as large. this behaviors
|
|
// has only been observed on directx5.
|
|
// This value is simply the maximum size for textures we request from
|
|
// the system, it has absolutely nothing to do with the size of primitives
|
|
// we're able to render, both concepts are totally independent from each other.
|
|
#define MAX_TEXTURE_SIZE (2048)
|
|
#define MIN_TEXTURE_SIZE (32)
|
|
//#define FAKE_MAX_NUMBER_TEXTURES (2)
|
|
//#define FAKE_MAX_TEXTURE_SIZE (512)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// includes
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
#include <vcl/syschild.hxx>
|
|
#include <vcl/window.hxx>
|
|
#include <canvas/debug.hxx>
|
|
#include <canvas/verbosetrace.hxx>
|
|
#include <canvas/elapsedtime.hxx>
|
|
#include <canvas/canvastools.hxx>
|
|
#include <canvas/rendering/icolorbuffer.hxx>
|
|
#include <canvas/rendering/isurface.hxx>
|
|
#include <canvas/rendering/irendermodule.hxx>
|
|
#include <tools/diagnose_ex.h>
|
|
#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>
|
|
|
|
#define COMPILE_MULTIMON_STUBS
|
|
|
|
#include "dx_rendermodule.hxx"
|
|
#include "dx_surfacegraphics.hxx"
|
|
#include <vcl/sysdata.hxx>
|
|
|
|
#undef WB_LEFT
|
|
#undef WB_RIGHT
|
|
|
|
#include "dx_impltools.hxx"
|
|
#include <malloc.h>
|
|
|
|
#if defined(DX_DEBUG_IMAGES)
|
|
# if OSL_DEBUG_LEVEL > 0
|
|
# include <imdebug.h>
|
|
# undef min
|
|
# undef max
|
|
# endif
|
|
#endif
|
|
|
|
#undef COMPILE_MULTIMON_STUBS
|
|
|
|
#include <stdio.h>
|
|
|
|
#define MONITOR_DEFAULTTONULL 0x00000000
|
|
#define MONITOR_DEFAULTTOPRIMARY 0x00000001
|
|
#define MONITOR_DEFAULTTONEAREST 0x00000002
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// 'dxcanvas' namespace
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace dxcanvas
|
|
{
|
|
namespace
|
|
{
|
|
bool doBlit( const ::basegfx::B2IPoint& rDestPos,
|
|
IDirectDrawSurface& rOutSurface,
|
|
const ::basegfx::B2IRange& rSourceArea,
|
|
IDirectDrawSurface& rSourceSurface,
|
|
DDBLTFX* pBltFx,
|
|
bool bForceSoftware )
|
|
{
|
|
if( !bForceSoftware )
|
|
{
|
|
// blit surface to backbuffer
|
|
RECT aOutRect =
|
|
{
|
|
rDestPos.getX(),
|
|
rDestPos.getY(),
|
|
rDestPos.getX() + static_cast<sal_Int32>(rSourceArea.getWidth()),
|
|
rDestPos.getY() + static_cast<sal_Int32>(rSourceArea.getHeight()),
|
|
};
|
|
RECT aSourceRect =
|
|
{
|
|
rSourceArea.getMinX(),
|
|
rSourceArea.getMinY(),
|
|
rSourceArea.getMaxX(),
|
|
rSourceArea.getMaxY()
|
|
};
|
|
|
|
if( SUCCEEDED(rOutSurface.Blt( &aOutRect,
|
|
&rSourceSurface,
|
|
&aSourceRect,
|
|
DDBLT_WAIT,
|
|
pBltFx )) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// failed, or forced to use SW copy. attempt manual copy.
|
|
bool bResult = false;
|
|
|
|
// lock source surface
|
|
DDSURFACEDESC aDescSrc;
|
|
rtl_fillMemory(&aDescSrc,sizeof(DDSURFACEDESC),0);
|
|
aDescSrc.dwSize = sizeof(DDSURFACEDESC);
|
|
const DWORD dwSrcFlags = DDLOCK_NOSYSLOCK|
|
|
DDLOCK_SURFACEMEMORYPTR|
|
|
DDLOCK_WAIT|
|
|
DDLOCK_READONLY;
|
|
if(SUCCEEDED(rSourceSurface.Lock(NULL,
|
|
&aDescSrc,
|
|
dwSrcFlags,
|
|
NULL)))
|
|
{
|
|
// lock destination surface
|
|
DDSURFACEDESC aDescDst;
|
|
rtl_fillMemory(&aDescDst,sizeof(DDSURFACEDESC),0);
|
|
aDescDst.dwSize = sizeof(DDSURFACEDESC);
|
|
const DWORD dwDstFlags = DDLOCK_NOSYSLOCK|
|
|
DDLOCK_SURFACEMEMORYPTR|
|
|
DDLOCK_WAIT|
|
|
DDLOCK_WRITEONLY;
|
|
if(SUCCEEDED(rOutSurface.Lock(NULL,
|
|
&aDescDst,
|
|
dwDstFlags,
|
|
NULL)))
|
|
{
|
|
sal_uInt32 nSrcFormat;
|
|
nSrcFormat = ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwRGBAlphaBitMask)<<12;
|
|
nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwRBitMask)<<8;
|
|
nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwGBitMask)<<4;
|
|
nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwBBitMask);
|
|
|
|
sal_uInt32 nDstFormat;
|
|
nDstFormat = ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwRGBAlphaBitMask)<<12;
|
|
nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwRBitMask)<<8;
|
|
nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwGBitMask)<<4;
|
|
nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwBBitMask);
|
|
|
|
// TODO(E1): Use numeric_cast to catch overflow here
|
|
const sal_uInt32 nWidth( static_cast<sal_uInt32>(
|
|
rSourceArea.getWidth() ) );
|
|
const sal_uInt32 nHeight( static_cast<sal_uInt32>(
|
|
rSourceArea.getHeight() ) );
|
|
|
|
if((nSrcFormat == 0x8888) && (nDstFormat == 0x0565))
|
|
{
|
|
// medium range 8888 to 0565 pixel format conversion.
|
|
bResult = true;
|
|
sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface +
|
|
rSourceArea.getMinY()*aDescSrc.lPitch +
|
|
(rSourceArea.getMinX()<<2);
|
|
sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface +
|
|
rDestPos.getY()*aDescDst.lPitch +
|
|
(rDestPos.getX()<<1);
|
|
for(sal_uInt32 y=0; y<nHeight; ++y)
|
|
{
|
|
sal_uInt32 *pSrcScanline = (sal_uInt32 *)pSrcSurface;
|
|
sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface;
|
|
for(sal_uInt32 x=0; x<nWidth; ++x)
|
|
{
|
|
sal_uInt32 srcPixel = *pSrcScanline++;
|
|
sal_uInt16 dstPixel;
|
|
dstPixel = (sal_uInt16)((srcPixel & 0x0000F8) >> 3);
|
|
dstPixel |= (srcPixel & 0x00FC00) >> 5;
|
|
dstPixel |= (srcPixel & 0xF80000) >> 8;
|
|
*pDstScanline++ = dstPixel;
|
|
}
|
|
pSrcSurface += aDescSrc.lPitch;
|
|
pDstSurface += aDescDst.lPitch;
|
|
}
|
|
}
|
|
else if((nSrcFormat == 0x8888) && (nDstFormat == 0x0888))
|
|
{
|
|
// medium range 8888 to 0888 pixel format conversion.
|
|
bResult = true;
|
|
sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface +
|
|
rSourceArea.getMinY()*aDescSrc.lPitch +
|
|
(rSourceArea.getMinX()<<2);
|
|
sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface +
|
|
rDestPos.getY()*aDescDst.lPitch +
|
|
(rDestPos.getX()<<2);
|
|
for(sal_uInt32 y=0; y<nHeight; ++y)
|
|
{
|
|
sal_uInt32 *pSrcScanline = (sal_uInt32 *)pSrcSurface;
|
|
sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface;
|
|
for(sal_uInt32 x=0; x<nWidth; ++x)
|
|
{
|
|
*pDstScanline++ = (sal_uInt16)*pSrcScanline++;
|
|
}
|
|
pSrcSurface += aDescSrc.lPitch;
|
|
pDstSurface += aDescDst.lPitch;
|
|
}
|
|
}
|
|
else if((nSrcFormat == 0x8888) && (nDstFormat == 0x1555))
|
|
{
|
|
// medium range 8888 to 1555 pixel format conversion.
|
|
bResult = true;
|
|
sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface +
|
|
rSourceArea.getMinY()*aDescSrc.lPitch +
|
|
(rSourceArea.getMinX()<<2);
|
|
sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface +
|
|
rDestPos.getY()*aDescDst.lPitch +
|
|
(rDestPos.getX()<<1);
|
|
for(sal_uInt32 y=0; y<nHeight; ++y)
|
|
{
|
|
sal_uInt32 *pSrcScanline = (sal_uInt32*)pSrcSurface;
|
|
sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface;
|
|
for(sal_uInt32 x=0; x<nWidth; ++x)
|
|
{
|
|
sal_uInt32 srcPixel = *pSrcScanline++;
|
|
sal_uInt16 dstPixel;
|
|
dstPixel = (sal_uInt16)((srcPixel & 0x000000F8) >> 3);
|
|
dstPixel |= (srcPixel & 0x0000F800) >> 6;
|
|
dstPixel |= (srcPixel & 0x00F80000) >> 9;
|
|
dstPixel |= (srcPixel & 0x80000000) >> 16;
|
|
*pDstScanline++ = dstPixel;
|
|
}
|
|
pSrcSurface += aDescSrc.lPitch;
|
|
pDstSurface += aDescDst.lPitch;
|
|
}
|
|
}
|
|
|
|
// unlock destination surface
|
|
rOutSurface.Unlock(NULL);
|
|
}
|
|
|
|
// unlock source surface
|
|
rSourceSurface.Unlock(NULL);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
void dumpSurface( const COMReference<IDirectDrawSurface> &pSurface, const char *szFilename )
|
|
{
|
|
if(!(pSurface.get()))
|
|
return;
|
|
|
|
DDSURFACEDESC aSurfaceDesc;
|
|
rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 );
|
|
aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
|
|
if( FAILED(pSurface->Lock( NULL,
|
|
&aSurfaceDesc,
|
|
DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY,
|
|
NULL)) )
|
|
return;
|
|
|
|
const std::size_t dwBitmapSize(aSurfaceDesc.dwWidth*aSurfaceDesc.dwHeight*4);
|
|
sal_uInt8 *pBuffer = static_cast<sal_uInt8 *>(_alloca(dwBitmapSize));
|
|
if(pBuffer)
|
|
{
|
|
sal_uInt8 *pSource = reinterpret_cast<sal_uInt8 *>(aSurfaceDesc.lpSurface);
|
|
sal_uInt8 *pDest = reinterpret_cast<sal_uInt8 *>(pBuffer);
|
|
const std::size_t dwDestPitch(aSurfaceDesc.dwWidth<<2);
|
|
pDest += aSurfaceDesc.dwHeight*dwDestPitch;
|
|
for(sal_uInt32 y=0; y<aSurfaceDesc.dwHeight; ++y)
|
|
{
|
|
pDest -= dwDestPitch;
|
|
rtl_copyMemory( pDest, pSource, dwDestPitch );
|
|
pSource += aSurfaceDesc.lPitch;
|
|
}
|
|
|
|
if(FILE *fp = fopen(szFilename,"wb"))
|
|
{
|
|
BITMAPINFOHEADER bitmapInfo;
|
|
|
|
bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);
|
|
bitmapInfo.biWidth = aSurfaceDesc.dwWidth;
|
|
bitmapInfo.biHeight = aSurfaceDesc.dwHeight;
|
|
bitmapInfo.biPlanes = 1;
|
|
bitmapInfo.biBitCount = 32;
|
|
bitmapInfo.biCompression = BI_RGB;
|
|
bitmapInfo.biSizeImage = 0;
|
|
bitmapInfo.biXPelsPerMeter = 0;
|
|
bitmapInfo.biYPelsPerMeter = 0;
|
|
bitmapInfo.biClrUsed = 0;
|
|
bitmapInfo.biClrImportant = 0;
|
|
|
|
const std::size_t dwFileSize(sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwBitmapSize);
|
|
|
|
BITMAPFILEHEADER header;
|
|
header.bfType = 'MB';
|
|
header.bfSize = dwFileSize;
|
|
header.bfReserved1 = 0;
|
|
header.bfReserved2 = 0;
|
|
header.bfOffBits = sizeof(BITMAPFILEHEADER) + bitmapInfo.biSize;
|
|
|
|
fwrite(&header,1,sizeof(BITMAPFILEHEADER),fp);
|
|
fwrite(&bitmapInfo,1,sizeof(BITMAPINFOHEADER),fp);
|
|
fwrite(pBuffer,1,dwBitmapSize,fp);
|
|
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
pSurface->Unlock(NULL);
|
|
}
|
|
|
|
void clearSurface( const COMReference<IDirectDrawSurface>& pSurface )
|
|
{
|
|
if(!(pSurface.is()))
|
|
return;
|
|
|
|
DDBLTFX aBltFx;
|
|
|
|
rtl_fillMemory( &aBltFx,
|
|
sizeof(DDBLTFX), 0 );
|
|
aBltFx.dwSize = sizeof(DDBLTFX);
|
|
aBltFx.dwFillColor = 0;
|
|
|
|
pSurface->Blt( NULL,
|
|
NULL,
|
|
NULL,
|
|
DDBLT_COLORFILL | DDBLT_WAIT,
|
|
&aBltFx );
|
|
}
|
|
|
|
// Define struct for MonitorEntry
|
|
struct MonitorEntry
|
|
{
|
|
GUID mnGUID;
|
|
HMONITOR mhMonitor;
|
|
MONITORINFO maMonitorInfo;
|
|
};
|
|
|
|
// define type for MonitorList
|
|
typedef ::std::vector< MonitorEntry > MonitorList;
|
|
|
|
// Win32 system callback for DirectDrawEnumerateExA call
|
|
BOOL WINAPI EnumerateExA_Callback( GUID FAR* lpGUID,
|
|
LPSTR /*lpDriverDescription*/,
|
|
LPSTR /*lpDriverName*/,
|
|
LPVOID lpContext,
|
|
HMONITOR hMonitor )
|
|
{
|
|
if(lpGUID)
|
|
{
|
|
MonitorList* pMonitorList = (MonitorList*)lpContext;
|
|
MonitorEntry aEntry;
|
|
|
|
aEntry.mnGUID = *lpGUID;
|
|
aEntry.mhMonitor = hMonitor;
|
|
aEntry.maMonitorInfo.cbSize = sizeof(MONITORINFO);
|
|
GetMonitorInfo( hMonitor,
|
|
&aEntry.maMonitorInfo );
|
|
|
|
pMonitorList->push_back(aEntry);
|
|
}
|
|
|
|
return DDENUMRET_OK;
|
|
}
|
|
|
|
void fillMonitorList( MonitorList& rMonitorList )
|
|
{
|
|
// Try to fill MonitorList. If neither lib or call to
|
|
// DirectDrawEnumerateExA does not exist, it's an old
|
|
// DX version (< 5.0), or system does not support
|
|
// multiple monitors.
|
|
HINSTANCE hInstance = LoadLibrary("ddraw.dll");
|
|
|
|
if(hInstance)
|
|
{
|
|
LPDIRECTDRAWENUMERATEEX lpDDEnumEx =
|
|
(LPDIRECTDRAWENUMERATEEX)GetProcAddress(hInstance,"DirectDrawEnumerateExA");
|
|
|
|
if(lpDDEnumEx)
|
|
lpDDEnumEx( (LPDDENUMCALLBACKEXA) EnumerateExA_Callback,
|
|
&rMonitorList,
|
|
DDENUM_ATTACHEDSECONDARYDEVICES );
|
|
|
|
FreeLibrary(hInstance);
|
|
}
|
|
}
|
|
|
|
IDirectDraw2* createDirectDraw( const MonitorList& rMonitorList,
|
|
MONITORINFO& rMonitorInfo,
|
|
HWND renderWindow )
|
|
{
|
|
GUID* gpSelectedDriverGUID = NULL;
|
|
|
|
// if we have multiple monitors, choose a gpSelectedDriverGUID from monitor list
|
|
HMONITOR hMonitor = MonitorFromWindow(renderWindow,
|
|
MONITOR_DEFAULTTONEAREST);
|
|
|
|
MonitorList::const_iterator aCurr = rMonitorList.begin();
|
|
const MonitorList::const_iterator aEnd = rMonitorList.end();
|
|
while( !gpSelectedDriverGUID && aCurr != aEnd )
|
|
{
|
|
if(hMonitor == aCurr->mhMonitor)
|
|
{
|
|
// This is the monitor we are running on
|
|
gpSelectedDriverGUID = const_cast<GUID*>(&aCurr->mnGUID);
|
|
rMonitorInfo = aCurr->maMonitorInfo;
|
|
}
|
|
|
|
++aCurr;
|
|
}
|
|
|
|
IDirectDraw* pDirectDraw;
|
|
if( FAILED( DirectDrawCreate( gpSelectedDriverGUID, &pDirectDraw, NULL )))
|
|
return NULL;
|
|
|
|
IDirectDraw2* pDirectDraw2;
|
|
if( FAILED( pDirectDraw->QueryInterface( IID_IDirectDraw2, (LPVOID*)&pDirectDraw2 )))
|
|
return NULL;
|
|
|
|
// queryInterface bumped up the refcount, so release the
|
|
// reference to the original IDirectDraw interface.
|
|
pDirectDraw->Release();
|
|
|
|
return pDirectDraw2;
|
|
}
|
|
|
|
HRESULT WINAPI EnumTextureFormatsCallback( LPDDSURFACEDESC pSurfaceDesc,
|
|
LPVOID pContext )
|
|
{
|
|
// dirty cast of given context back to result ModeSelectContext
|
|
DDPIXELFORMAT* pResult = (DDPIXELFORMAT*)pContext;
|
|
|
|
if( pResult == NULL || pSurfaceDesc == NULL )
|
|
return DDENUMRET_CANCEL;
|
|
|
|
VERBOSE_TRACE( "EnumTextureFormatsCallback: advertised texture format has dwRGBBitCount %d, dwRBitMask %x, "
|
|
"dwGBitMask %x, dwBBitMask %x and dwRGBAlphaBitMask %x. The format uses %s alpha.",
|
|
pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount,
|
|
pSurfaceDesc->ddpfPixelFormat.dwRBitMask,
|
|
pSurfaceDesc->ddpfPixelFormat.dwGBitMask,
|
|
pSurfaceDesc->ddpfPixelFormat.dwBBitMask,
|
|
pSurfaceDesc->ddpfPixelFormat.dwRGBAlphaBitMask,
|
|
pSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPREMULT ? "premultiplied" : "non-premultiplied" );
|
|
|
|
// Only accept RGB surfaces with alpha channel
|
|
if( (DDPF_ALPHAPIXELS | DDPF_RGB) ==
|
|
(pSurfaceDesc->ddpfPixelFormat.dwFlags & (DDPF_ALPHAPIXELS | DDPF_RGB)) )
|
|
{
|
|
// ignore formats with the DDPF_ALPHAPREMULT flag
|
|
if(!(pSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPREMULT))
|
|
{
|
|
// take widest alpha channel available
|
|
if( pSurfaceDesc->ddpfPixelFormat.dwAlphaBitDepth > pResult->dwAlphaBitDepth )
|
|
{
|
|
// take new format
|
|
rtl_copyMemory( pResult, &pSurfaceDesc->ddpfPixelFormat, sizeof(DDPIXELFORMAT) );
|
|
}
|
|
else if( pSurfaceDesc->ddpfPixelFormat.dwAlphaBitDepth == pResult->dwAlphaBitDepth )
|
|
{
|
|
// tie-breaking: take highest bitcount
|
|
if( pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount > pResult->dwRGBBitCount )
|
|
{
|
|
// take new format
|
|
rtl_copyMemory( pResult, &pSurfaceDesc->ddpfPixelFormat, sizeof(DDPIXELFORMAT) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return DDENUMRET_OK;
|
|
}
|
|
|
|
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();
|
|
|
|
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<IDirectDrawSurface> mpSurface;
|
|
COMReference<IDirect3DTexture2> mpTexture;
|
|
|
|
::basegfx::B2IVector maSize;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// Default implementation of IDXRenderModule
|
|
class DXRenderModule : public IDXRenderModule
|
|
{
|
|
public:
|
|
explicit DXRenderModule( const ::Window& rWindow );
|
|
|
|
virtual void lock() const { maMutex.acquire(); }
|
|
virtual void unlock() const { maMutex.release(); }
|
|
|
|
virtual COMReference<IDirectDrawSurface>
|
|
createSystemMemorySurface( const ::basegfx::B2IVector& rSize );
|
|
|
|
virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
|
|
const ::basegfx::B2IRectangle& rCurrWindowArea );
|
|
|
|
virtual void resize( const ::basegfx::B2IRange& rect );
|
|
virtual HWND getHWND() const { return mhWnd; }
|
|
virtual void disposing();
|
|
virtual void screenShot();
|
|
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();
|
|
|
|
const D3DDEVICEDESC& getDeviceDesc() const { return maDeviceDesc; }
|
|
const DDPIXELFORMAT& getTextureFormat() const { return maTextureFormat; }
|
|
COMReference<IDirectDraw2> getDirectDraw() { return mpDirectDraw; }
|
|
COMReference< IDirect3DDevice2 > getDevice() { return mpDirect3DDevice; }
|
|
|
|
void flushVertexCache();
|
|
|
|
struct ModeSelectContext
|
|
{
|
|
DDSURFACEDESC selectedDesc;
|
|
::basegfx::B2ISize requestedSize;
|
|
};
|
|
|
|
/** Query actual size of the device
|
|
|
|
This is especially interesting for fullscreen devices
|
|
*/
|
|
::basegfx::B2ISize getFramebufferSize() const;
|
|
|
|
/** Query the amount of memory available for new surfaces
|
|
|
|
This might differ from getAvailableTextureMem()
|
|
@see getAvailableTextureMem()
|
|
|
|
@param bWithAGPMema
|
|
When true, returned value includes non-local,
|
|
i.e. AGP-able memory, too.
|
|
|
|
@return the amount of free surface mem
|
|
*/
|
|
std::size_t getAvailableSurfaceMem( bool bWithAGPMem=true ) const;
|
|
|
|
/** Query the amount of memory available for new textures
|
|
|
|
This might differ from getAvailableSurfaceMem()
|
|
@see getAvailableSurfaceMem()
|
|
|
|
@param bWithAGPMema
|
|
When true, returned value includes non-local,
|
|
i.e. AGP-able memory, too.
|
|
|
|
@return the amount of free texture mem
|
|
*/
|
|
std::size_t getAvailableTextureMem( bool bWithAGPMem=true ) const;
|
|
|
|
private:
|
|
bool queryCaps();
|
|
bool validateCaps();
|
|
bool setup3DDevice();
|
|
unsigned int getDisplayFormat() const;
|
|
|
|
void convert2Screen( ::basegfx::B2IPoint& io_rDestPos,
|
|
::basegfx::B2IRange& io_rDestArea );
|
|
|
|
void renderInfoText( const ::rtl::OUString& rStr,
|
|
const Gdiplus::PointF& rPos ) const;
|
|
void renderFPSCounter() const;
|
|
void renderMemAvailable() const;
|
|
|
|
bool create( const ::Window& rWindow );
|
|
bool validateMainSurfaces();
|
|
|
|
/** 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;
|
|
::boost::scoped_ptr<SystemChildWindow> mpWindow;
|
|
::basegfx::B2IVector maSize;
|
|
|
|
ModeSelectContext maSelectedFullscreenMode;
|
|
DDPIXELFORMAT maTextureFormat;
|
|
|
|
MONITORINFO maMonitorInfo; // monitor info for mpDirectDraw's monitor
|
|
COMReference<IDirectDraw2> mpDirectDraw;
|
|
COMReference<IDirectDrawSurface> mpPrimarySurface;
|
|
COMReference<IDirectDrawSurface> mpBackBufferSurface;
|
|
|
|
COMReference< IDirect3D2 > mpDirect3D;
|
|
COMReference< IDirect3DDevice2 > mpDirect3DDevice;
|
|
|
|
mutable ::canvas::tools::ElapsedTime maLastUpdate; // for the frame counter
|
|
|
|
D3DDEVICEDESC maDeviceDesc;
|
|
|
|
typedef std::vector<canvas::Vertex> vertexCache_t;
|
|
vertexCache_t maVertexCache;
|
|
std::size_t mnCount;
|
|
|
|
int mnBeginSceneCount;
|
|
|
|
const bool mbPageFlipping;
|
|
bool mbHasNoTearingBlt;
|
|
bool mbError;
|
|
PrimitiveType meType;
|
|
|
|
::canvas::ISurfaceSharedPtr mpTexture;
|
|
::basegfx::B2IVector maPageSize;
|
|
};
|
|
|
|
::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),
|
|
mpSurface(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");
|
|
|
|
const D3DDEVICEDESC &deviceDesc = rRenderModule.getDeviceDesc();
|
|
|
|
DDSURFACEDESC aSurfaceDesc;
|
|
rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 );
|
|
aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
|
|
aSurfaceDesc.dwWidth = ::std::min(deviceDesc.dwMaxTextureWidth,::canvas::tools::nextPow2(rSize.getX()));
|
|
aSurfaceDesc.dwHeight = ::std::min(deviceDesc.dwMaxTextureHeight,::canvas::tools::nextPow2(rSize.getY()));
|
|
aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE |
|
|
DDSCAPS_VIDEOMEMORY |
|
|
DDSCAPS_LOCALVIDMEM;
|
|
rtl_copyMemory(&aSurfaceDesc.ddpfPixelFormat,&rRenderModule.getTextureFormat(),sizeof(DDPIXELFORMAT));
|
|
|
|
IDirectDrawSurface *pSurface;
|
|
COMReference<IDirectDraw2> pDirectDraw(rRenderModule.getDirectDraw());
|
|
HRESULT hr = pDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);
|
|
if(FAILED(hr))
|
|
{
|
|
// if the call failed due to 'out of videomemory',
|
|
// retry with request for AGP memory.
|
|
if(DDERR_OUTOFVIDEOMEMORY == hr)
|
|
{
|
|
aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE |
|
|
DDSCAPS_VIDEOMEMORY |
|
|
DDSCAPS_NONLOCALVIDMEM;
|
|
hr = pDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
IDirect3DTexture2* pTexture;
|
|
if( FAILED(pSurface->QueryInterface(IID_IDirect3DTexture2, (LPVOID *)&pTexture)) )
|
|
{
|
|
pSurface->Release();
|
|
return;
|
|
}
|
|
|
|
maSize.setX(aSurfaceDesc.dwWidth);
|
|
maSize.setY(aSurfaceDesc.dwHeight);
|
|
|
|
mpSurface=COMReference<IDirectDrawSurface>(pSurface);
|
|
mpTexture=COMReference<IDirect3DTexture2>(pTexture);
|
|
|
|
// #122683# Clear texture, to avoid ugly artifacts at the
|
|
// border to invisible sprite areas (note that the textures
|
|
// are usually only partly utilized).
|
|
clearSurface( mpSurface );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXSurface::~DXSurface
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DXSurface::~DXSurface()
|
|
{
|
|
ImplRenderModuleGuard aGuard( mrRenderModule );
|
|
|
|
#ifdef FAKE_MAX_NUMBER_TEXTURES
|
|
gNumSurfaces--;
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXSurface::selectTexture
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool DXSurface::selectTexture()
|
|
{
|
|
ImplRenderModuleGuard aGuard( mrRenderModule );
|
|
|
|
mrRenderModule.flushVertexCache();
|
|
|
|
D3DTEXTUREHANDLE aTextureHandle;
|
|
if(FAILED(mpTexture->GetHandle(
|
|
mrRenderModule.getDevice().get(),
|
|
&aTextureHandle)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// select texture for next primitive
|
|
if(FAILED(mrRenderModule.getDevice()->SetRenderState(
|
|
D3DRENDERSTATE_TEXTUREHANDLE,aTextureHandle)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if defined(DX_DEBUG_IMAGES)
|
|
# if OSL_DEBUG_LEVEL > 0
|
|
if( mpSurface.is() )
|
|
{
|
|
DDSURFACEDESC aSurfaceDesc;
|
|
rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 );
|
|
aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
|
|
if( SUCCEEDED(mpSurface->Lock( NULL,
|
|
&aSurfaceDesc,
|
|
DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY,
|
|
NULL)) )
|
|
{
|
|
imdebug( "rgba w=%d h=%d %p",
|
|
aSurfaceDesc.dwWidth,
|
|
aSurfaceDesc.dwHeight,
|
|
aSurfaceDesc.lpSurface );
|
|
|
|
mpSurface->Unlock(NULL);
|
|
}
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXSurface::isValid
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool DXSurface::isValid()
|
|
{
|
|
ImplRenderModuleGuard aGuard( mrRenderModule );
|
|
|
|
if(!(mpSurface.is()))
|
|
return false;
|
|
|
|
if(mpSurface->IsLost() == DDERR_SURFACELOST)
|
|
{
|
|
mpSurface->Restore();
|
|
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;
|
|
|
|
DDSURFACEDESC aSurfaceDesc;
|
|
rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 );
|
|
aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
|
|
// TODO(P2): only lock the region we want to update
|
|
if( FAILED(mpSurface->Lock( NULL,
|
|
&aSurfaceDesc,
|
|
DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_WRITEONLY,
|
|
NULL)) )
|
|
return false;
|
|
|
|
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*)aSurfaceDesc.lpSurface+
|
|
(rDestPos.getY()*aSurfaceDesc.lPitch) +
|
|
(4*rDestPos.getX()));
|
|
|
|
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)
|
|
{
|
|
rtl_copyMemory(pDst,pImage,nNumBytesToCopy);
|
|
|
|
pDst += aSurfaceDesc.lPitch;
|
|
pImage += nSourcePitchInBytes;
|
|
}
|
|
}
|
|
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*)aSurfaceDesc.lpSurface+
|
|
(rDestPos.getY()*aSurfaceDesc.lPitch) +
|
|
(4*rDestPos.getX()));
|
|
|
|
const sal_uInt64 nNumColumns(rSourceRect.getWidth());
|
|
const sal_uInt64 nNumLines(rSourceRect.getHeight());
|
|
for(sal_uInt32 i=0; i<nNumLines; ++i)
|
|
{
|
|
sal_uInt32 *pDstScanline = reinterpret_cast<sal_uInt32 *>(pDst);
|
|
sal_uInt8 *pSrcScanline = reinterpret_cast<sal_uInt8 *>(pImage);
|
|
for(sal_uInt32 x=0; x<nNumColumns; ++x)
|
|
{
|
|
sal_uInt32 color(0xFF000000);
|
|
color |= pSrcScanline[2]<<16;
|
|
color |= pSrcScanline[1]<<8;
|
|
color |= pSrcScanline[0];
|
|
pSrcScanline += 3;
|
|
*pDstScanline++ = color;
|
|
}
|
|
|
|
pDst += aSurfaceDesc.lPitch;
|
|
pImage += nSourcePitchInBytes;
|
|
}
|
|
}
|
|
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*)aSurfaceDesc.lpSurface+
|
|
(rDestPos.getY()*aSurfaceDesc.lPitch) +
|
|
(4*rDestPos.getX()));
|
|
|
|
const sal_uInt64 nNumLines(rSourceRect.getHeight());
|
|
|
|
for(sal_uInt32 i=0; i<nNumLines; ++i)
|
|
{
|
|
sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage);
|
|
sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst);
|
|
for(sal_uInt32 j=0; j<rSourceRect.getWidth(); ++j)
|
|
pDst32[j] = 0xFF000000 | pSrc32[j];
|
|
|
|
pDst += aSurfaceDesc.lPitch;
|
|
pImage += nSourcePitchInBytes;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ENSURE_OR_RETURN_FALSE(false,
|
|
"DXSurface::update(): Unknown/unimplemented buffer format" );
|
|
break;
|
|
}
|
|
|
|
rSource.unlock();
|
|
}
|
|
|
|
return SUCCEEDED(mpSurface->Unlock(NULL));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXSurface::getSize
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
::basegfx::B2IVector DXSurface::getSize()
|
|
{
|
|
return maSize;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::DXRenderModule
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DXRenderModule::DXRenderModule( const ::Window& rWindow ) :
|
|
mhWnd(0),
|
|
mpWindow(),
|
|
maSize(),
|
|
maSelectedFullscreenMode(),
|
|
maTextureFormat(),
|
|
maMonitorInfo(),
|
|
mpDirectDraw(),
|
|
mpPrimarySurface(),
|
|
mpBackBufferSurface(),
|
|
mpDirect3D(),
|
|
mpDirect3DDevice(),
|
|
maLastUpdate(),
|
|
maDeviceDesc(),
|
|
maVertexCache(),
|
|
mnCount(0),
|
|
mnBeginSceneCount(0),
|
|
mbPageFlipping( false ),
|
|
mbHasNoTearingBlt( false ),
|
|
mbError( false ),
|
|
meType( PRIMITIVE_TYPE_UNKNOWN ),
|
|
mpTexture(),
|
|
maPageSize()
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
if(!(create(rWindow)))
|
|
{
|
|
throw lang::NoSupportException(
|
|
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
|
|
"Could not create DirectX device!") ),NULL);
|
|
}
|
|
|
|
// allocate a single texture surface which can be used later.
|
|
// we also use this to calibrate the page size.
|
|
::basegfx::B2IVector aPageSize(
|
|
::std::min(
|
|
static_cast<sal_uInt32>(maDeviceDesc.dwMaxTextureWidth),
|
|
static_cast<sal_uInt32>(MAX_TEXTURE_SIZE)),
|
|
::std::min(
|
|
static_cast<sal_uInt32>(maDeviceDesc.dwMaxTextureHeight),
|
|
static_cast<sal_uInt32>(MAX_TEXTURE_SIZE)));
|
|
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(
|
|
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
|
|
"Could not create DirectX device!") ),NULL);
|
|
}
|
|
}
|
|
maPageSize=aPageSize;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// 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( sal_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) ),
|
|
"DXRenderModuleDXRenderModuleWin32() 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());
|
|
|
|
MonitorList aMonitorList;
|
|
fillMonitorList( aMonitorList );
|
|
|
|
mpDirectDraw = COMReference<IDirectDraw2>(
|
|
createDirectDraw(aMonitorList, maMonitorInfo, mhWnd));
|
|
|
|
if(!mpDirectDraw.is())
|
|
return false;
|
|
|
|
if( !queryCaps() )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): GetCaps failed" );
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
if( !validateCaps() )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): Insufficient DirectX capabilities, failed" );
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
if( FAILED( mpDirectDraw->SetCooperativeLevel( mhWnd,
|
|
DDSCL_NORMAL|DDSCL_MULTITHREADED|DDSCL_FPUPRESERVE ) ) )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): SetCooperativeLevel failed" );
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
// setup query struct
|
|
rtl_fillMemory( &maSelectedFullscreenMode.selectedDesc,
|
|
sizeof(DDSURFACEDESC), 0 );
|
|
maSelectedFullscreenMode.selectedDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
|
|
// read current display mode, e.g. for screen dimension
|
|
if( FAILED( mpDirectDraw->GetDisplayMode( &maSelectedFullscreenMode.selectedDesc )) )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): GetDisplayMode failed" );
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
// check for supported primary surface formats...
|
|
unsigned int nDisplayFormat = getDisplayFormat() & 0x00000FFF;
|
|
if(nDisplayFormat != 0x888 && nDisplayFormat != 0x565)
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): Unsupported DisplayFormat" );
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
// create primary surface reference
|
|
DDSURFACEDESC aSurfaceDesc;
|
|
IDirectDrawSurface* pPrimarySurface;
|
|
|
|
rtl_fillMemory( &aSurfaceDesc,
|
|
sizeof(DDSURFACEDESC), 0 );
|
|
aSurfaceDesc.dwSize = sizeof(aSurfaceDesc);
|
|
aSurfaceDesc.dwFlags = DDSD_CAPS;
|
|
aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE;
|
|
|
|
if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pPrimarySurface, NULL)) )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): CreateSurface failed" );
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
mpPrimarySurface = COMReference< IDirectDrawSurface >(pPrimarySurface);
|
|
|
|
// create a Clipper and associate it with the primary surface
|
|
// and the render window
|
|
LPDIRECTDRAWCLIPPER pClipper;
|
|
if( FAILED(mpDirectDraw->CreateClipper( 0, &pClipper, NULL )) )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): CreateClipper failed" );
|
|
mpPrimarySurface.reset();
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
if( FAILED(pClipper->SetHWnd(0, mhWnd)) )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): Clipper->SetHWnd failed" );
|
|
pClipper->Release();
|
|
mpPrimarySurface.reset();
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
if( FAILED(mpPrimarySurface->SetClipper( pClipper )) )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): SetClipper failed" );
|
|
pClipper->Release();
|
|
mpPrimarySurface.reset();
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
// clipper is now owned by mpPrimarySurface, release our reference
|
|
pClipper->Release();
|
|
|
|
// TODO(F3): Check whether palette needs any setup here
|
|
|
|
// get us a backbuffer for simulated flipping
|
|
IDirectDrawSurface* pSurface;
|
|
|
|
// 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.
|
|
const ::basegfx::B2ISize aSize( getFramebufferSize() );
|
|
|
|
rtl_fillMemory( &aSurfaceDesc,
|
|
sizeof(DDSURFACEDESC), 0 );
|
|
aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
|
|
aSurfaceDesc.dwHeight= aSize.getY();
|
|
aSurfaceDesc.dwWidth = aSize.getX();
|
|
|
|
aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
|
|
|
|
HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);
|
|
|
|
if( FAILED( nRes ) )
|
|
{
|
|
if( nRes == DDERR_OUTOFVIDEOMEMORY )
|
|
{
|
|
// local vid mem failed. Maybe AGP mem works?
|
|
aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
|
|
if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL)) )
|
|
{
|
|
// no chance, go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" );
|
|
mpPrimarySurface.reset();
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer reverted to non-local video mem" );
|
|
}
|
|
else
|
|
{
|
|
// no chance, go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" );
|
|
mpPrimarySurface.reset();
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
VERBOSE_TRACE( "Device::Device(): created backbuffer of size %d times %d pixel",
|
|
aSurfaceDesc.dwWidth,
|
|
aSurfaceDesc.dwHeight );
|
|
|
|
mpBackBufferSurface = COMReference< IDirectDrawSurface >(pSurface);
|
|
clearSurface(mpBackBufferSurface);
|
|
|
|
if( !setup3DDevice() )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::Device(): setup3DDevice failed" );
|
|
mpBackBufferSurface.reset();
|
|
mpPrimarySurface.reset();
|
|
mpDirectDraw.reset();
|
|
return false;
|
|
}
|
|
|
|
mpWindow->Show();
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::getSize
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
::basegfx::B2ISize DXRenderModule::getFramebufferSize() const
|
|
{
|
|
return mpDirectDraw.is() ?
|
|
::basegfx::B2ISize( maSelectedFullscreenMode.selectedDesc.dwWidth,
|
|
maSelectedFullscreenMode.selectedDesc.dwHeight ) :
|
|
::basegfx::B2ISize();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::setup3DDevice
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool DXRenderModule::setup3DDevice()
|
|
{
|
|
// create and setup 3D device
|
|
// ==========================
|
|
LPDIRECT3D2 pDirect3D;
|
|
if( FAILED( mpDirectDraw->QueryInterface( IID_IDirect3D2, (LPVOID*)&pDirect3D ) ) )
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): QueryInterface() for Direct3D failed" );
|
|
return false;
|
|
}
|
|
|
|
mpDirect3D = COMReference< IDirect3D2 >(pDirect3D);
|
|
|
|
LPDIRECT3DDEVICE2 pDirect3DDevice;
|
|
// try HW-accelerated device first
|
|
if( FAILED(mpDirect3D->CreateDevice( IID_IDirect3DHALDevice,
|
|
mpBackBufferSurface.get(),
|
|
&pDirect3DDevice )) )
|
|
{
|
|
// no HW 3D support - go defunct, and exit
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): CreateDevice() for HW Direct3D rendering failed" );
|
|
mpDirect3D.reset();
|
|
return false;
|
|
}
|
|
|
|
D3DDEVICEDESC aHELDeviceDesc;
|
|
rtl_fillMemory(&maDeviceDesc,sizeof(maDeviceDesc),0);
|
|
rtl_fillMemory(&aHELDeviceDesc,sizeof(aHELDeviceDesc),0);
|
|
maDeviceDesc.dwSize = sizeof(maDeviceDesc);
|
|
aHELDeviceDesc.dwSize = sizeof(aHELDeviceDesc);
|
|
if(FAILED(pDirect3DDevice->GetCaps(&maDeviceDesc,&aHELDeviceDesc)))
|
|
{
|
|
// go defunct, and exit
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): GetCaps() for Direct3DDevice failed" );
|
|
mpDirect3D.reset();
|
|
return false;
|
|
}
|
|
|
|
mpDirect3DDevice = COMReference< IDirect3DDevice2 >(pDirect3DDevice);
|
|
|
|
// select appropriate texture format (_need_ alpha channel here)
|
|
rtl_fillMemory( &maTextureFormat,
|
|
sizeof(DDPIXELFORMAT), 0 );
|
|
maTextureFormat.dwSize = sizeof(DDPIXELFORMAT);
|
|
if( SUCCEEDED(mpDirect3DDevice->EnumTextureFormats( EnumTextureFormatsCallback, &maTextureFormat )) )
|
|
{
|
|
bool bSupportedFormat = true;
|
|
if((maTextureFormat.dwFlags & (DDPF_ALPHAPIXELS | DDPF_RGB)) != (DDPF_ALPHAPIXELS | DDPF_RGB))
|
|
bSupportedFormat = false;
|
|
else if(maTextureFormat.dwRGBAlphaBitMask != 0xFF000000)
|
|
bSupportedFormat = false;
|
|
else if(maTextureFormat.dwRBitMask != 0x00FF0000)
|
|
bSupportedFormat = false;
|
|
else if(maTextureFormat.dwGBitMask != 0x0000FF00)
|
|
bSupportedFormat = false;
|
|
else if(maTextureFormat.dwBBitMask != 0x000000FF)
|
|
bSupportedFormat = false;
|
|
|
|
if(bSupportedFormat)
|
|
{
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): chose texture format dwRGBBitCount %d, dwRBitMask %x, "
|
|
"dwGBitMask %x, dwBBitMask %x and dwRGBAlphaBitMask %x. The texture uses %s alpha.",
|
|
maTextureFormat.dwRGBBitCount,
|
|
maTextureFormat.dwRBitMask,
|
|
maTextureFormat.dwGBitMask,
|
|
maTextureFormat.dwBBitMask,
|
|
maTextureFormat.dwRGBAlphaBitMask,
|
|
maTextureFormat.dwFlags & DDPF_ALPHAPREMULT ? "premultiplied" : "non-premultiplied" );
|
|
|
|
// setup the device (with as much as we can possibly do here)
|
|
// ==========================================================
|
|
|
|
LPDIRECT3DVIEWPORT2 pViewport;
|
|
|
|
if( SUCCEEDED(mpDirect3D->CreateViewport( &pViewport, NULL )) )
|
|
{
|
|
if( SUCCEEDED(mpDirect3DDevice->AddViewport( pViewport )) )
|
|
{
|
|
// setup viewport (to whole backbuffer)
|
|
D3DVIEWPORT2 aViewport;
|
|
|
|
aViewport.dwSize = sizeof(D3DVIEWPORT2);
|
|
aViewport.dwX = 0;
|
|
aViewport.dwY = 0;
|
|
aViewport.dwWidth = maSelectedFullscreenMode.selectedDesc.dwWidth;
|
|
aViewport.dwHeight = maSelectedFullscreenMode.selectedDesc.dwHeight;
|
|
aViewport.dvClipX = -1.0;
|
|
aViewport.dvClipY = -1.0;
|
|
aViewport.dvClipWidth = 2.0;
|
|
aViewport.dvClipHeight = 2.0;
|
|
aViewport.dvMinZ = 0.0;
|
|
aViewport.dvMaxZ = 1.0;
|
|
|
|
if( SUCCEEDED(pViewport->SetViewport2( &aViewport )) )
|
|
{
|
|
if( SUCCEEDED(mpDirect3DDevice->SetCurrentViewport( pViewport )) )
|
|
{
|
|
// Viewport was handed over to 3DDevice, thus we can release now
|
|
pViewport->Release();
|
|
|
|
// currently, no need for any
|
|
// matrix or light source
|
|
// setup, since we only render
|
|
// transformed&lighted
|
|
// vertices
|
|
|
|
// done; successfully
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): SetCurrentViewport failed" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): SetViewport2 failed" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): AddViewport failed" );
|
|
}
|
|
|
|
pViewport->Release();
|
|
}
|
|
else
|
|
{
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): CreateViewport failed" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): No supported pixelformat" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VERBOSE_TRACE( "Device::setup3DDevice(): EnumTextureFormats failed" );
|
|
}
|
|
|
|
// go defunct, and exit
|
|
mpDirect3DDevice.reset();
|
|
mpDirect3D.reset();
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::queryCaps
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool DXRenderModule::queryCaps()
|
|
{
|
|
DDCAPS aHWCaps;
|
|
DDCAPS aHELCaps;
|
|
|
|
rtl_fillMemory( &aHWCaps,
|
|
sizeof(aHWCaps), 0 );
|
|
rtl_fillMemory( &aHELCaps,
|
|
sizeof(aHELCaps), 0 );
|
|
aHWCaps.dwSize = sizeof( aHWCaps );
|
|
aHELCaps.dwSize = sizeof( aHELCaps );
|
|
|
|
if( FAILED( mpDirectDraw->GetCaps( &aHWCaps,
|
|
&aHELCaps ) ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
mbHasNoTearingBlt = aHWCaps.dwFXCaps & DDBLTFX_NOTEARING;
|
|
|
|
VERBOSE_TRACE( "dxcanvas initialization: %d bytes VRAM free for surfaces (%d with AGP mem), "
|
|
"%d bytes VRAM free for textures (%d with AGP mem)",
|
|
getAvailableSurfaceMem( false ),
|
|
getAvailableSurfaceMem( true ),
|
|
getAvailableTextureMem( false ),
|
|
getAvailableTextureMem( true ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::validateCaps
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool DXRenderModule::validateCaps()
|
|
{
|
|
// TODO(E3): Validate HW capabilities. Depending on primary
|
|
// surface size, reject HW e.g. on the grounds of insufficient
|
|
// VRAM.
|
|
|
|
// setup query struct
|
|
DDSURFACEDESC desc;
|
|
rtl_fillMemory(&desc,sizeof(DDSURFACEDESC),0);
|
|
desc.dwSize = sizeof(DDSURFACEDESC);
|
|
|
|
// read current display mode, e.g. for screen dimension
|
|
if(FAILED( mpDirectDraw->GetDisplayMode(&desc)))
|
|
return false;
|
|
|
|
// simple heuristic: we need at least 3 times the desktop
|
|
// resolution based on ARGB color values...
|
|
std::size_t nMinimumVRAMSize = ((desc.dwWidth*desc.dwHeight)<<2)*3;
|
|
if(getAvailableSurfaceMem() < nMinimumVRAMSize)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::getDisplayFormat
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
unsigned int DXRenderModule::getDisplayFormat() const
|
|
{
|
|
unsigned int nFormat;
|
|
nFormat = ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwRGBAlphaBitMask)<<12;
|
|
nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwRBitMask)<<8;
|
|
nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwGBitMask)<<4;
|
|
nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwBBitMask);
|
|
return nFormat;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::getAvailableSurfaceMem
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
std::size_t DXRenderModule::getAvailableSurfaceMem( bool bWithAGPMem ) const
|
|
{
|
|
if( !mpDirectDraw.is() )
|
|
return 0;
|
|
|
|
std::size_t nRes( 0 );
|
|
|
|
DDSCAPS aSurfaceCaps;
|
|
DWORD nTotal, nFree;
|
|
|
|
// real VRAM (const_cast, since GetAvailableVidMem is non-const)
|
|
aSurfaceCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
|
|
if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) )
|
|
return 0;
|
|
|
|
nRes += nFree;
|
|
|
|
if( bWithAGPMem )
|
|
{
|
|
// AGP RAM (const_cast, since GetAvailableVidMem is non-const)
|
|
aSurfaceCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
|
|
if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) )
|
|
return 0;
|
|
|
|
nRes += nFree;
|
|
}
|
|
|
|
return nRes;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::getAvailableTextureMem
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
std::size_t DXRenderModule::getAvailableTextureMem( bool bWithAGPMem ) const
|
|
{
|
|
if( !mpDirectDraw.is() )
|
|
return 0;
|
|
|
|
std::size_t nRes( 0 );
|
|
|
|
DDSCAPS aSurfaceCaps;
|
|
DWORD nTotal, nFree;
|
|
|
|
// TODO(F1): Check if flags are applicable
|
|
|
|
// real VRAM (const_cast, since GetAvailableVidMem is non-const)
|
|
aSurfaceCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
|
|
if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) )
|
|
return 0;
|
|
|
|
nRes += nFree;
|
|
|
|
if( bWithAGPMem )
|
|
{
|
|
// AGP RAM (const_cast, since GetAvailableVidMem is non-const)
|
|
aSurfaceCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
|
|
if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) )
|
|
return 0;
|
|
|
|
nRes += nFree;
|
|
}
|
|
|
|
// TODO(F1): Add pool mem
|
|
|
|
return nRes;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::convert2Screen
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DXRenderModule::convert2Screen( ::basegfx::B2IPoint& io_rDestPos,
|
|
::basegfx::B2IRange& io_rDestArea )
|
|
{
|
|
POINT aPoint = { 0, 0 };
|
|
ClientToScreen( mhWnd, &aPoint );
|
|
|
|
// i52230 make sure given screen coordinate is relative to
|
|
// this monitor's area (the device rendering is always
|
|
// contained to a single monitor)
|
|
aPoint.x -= maMonitorInfo.rcMonitor.left;
|
|
aPoint.y -= maMonitorInfo.rcMonitor.top;
|
|
|
|
io_rDestPos.setX( io_rDestPos.getX() + aPoint.x );
|
|
io_rDestPos.setY( io_rDestPos.getY() + aPoint.y );
|
|
|
|
const ::basegfx::B2ISize& rSize( getFramebufferSize() );
|
|
|
|
// calc output bounds (clip against framebuffer bounds)
|
|
io_rDestArea = ::basegfx::B2IRange(
|
|
::std::max( sal_Int32(0),
|
|
::std::min( sal_Int32(rSize.getX()),
|
|
sal_Int32(io_rDestArea.getMinX() + aPoint.x) ) ),
|
|
::std::max( sal_Int32(0),
|
|
::std::min( sal_Int32(rSize.getY()),
|
|
sal_Int32(io_rDestArea.getMinY() + aPoint.y) ) ),
|
|
::std::max( sal_Int32(0),
|
|
::std::min( sal_Int32(rSize.getX()),
|
|
sal_Int32(io_rDestArea.getMaxX() + aPoint.x) ) ),
|
|
::std::max( sal_Int32(0),
|
|
::std::min( sal_Int32(rSize.getY()),
|
|
sal_Int32(io_rDestArea.getMaxY() + aPoint.y) ) ) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::createSystemMemorySurface
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
COMReference<IDirectDrawSurface> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
|
|
{
|
|
DDSURFACEDESC aSurfaceDesc;
|
|
IDirectDrawSurface* pSurface;
|
|
|
|
aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;;
|
|
aSurfaceDesc.dwWidth = rSize.getX();
|
|
aSurfaceDesc.dwHeight= rSize.getY();
|
|
|
|
rtl_copyMemory( &aSurfaceDesc.ddpfPixelFormat, &maTextureFormat, sizeof(DDPIXELFORMAT) );
|
|
|
|
aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
|
|
|
|
HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);
|
|
if(FAILED(nRes))
|
|
return COMReference<IDirectDrawSurface>(NULL);
|
|
|
|
return COMReference<IDirectDrawSurface>(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 );
|
|
|
|
// see if the main surfaces got lost. if so, try to
|
|
// restore them. bail out if this operation fails.
|
|
if(!(validateMainSurfaces()))
|
|
return false;
|
|
|
|
flushVertexCache();
|
|
|
|
ENSURE_OR_THROW( !mnBeginSceneCount,
|
|
"Device::flip(): within 3D scene" );
|
|
|
|
// TODO(E3): handle DX errors more thoroughly. For fullscreen
|
|
// exclusive mode, actually even our primary surface can get
|
|
// lost and needs restore!
|
|
|
|
if( mpDirectDraw.is() &&
|
|
mpPrimarySurface.is() &&
|
|
mpBackBufferSurface.is() )
|
|
{
|
|
// ignore area and offset for page flipping device
|
|
if( mbPageFlipping )
|
|
{
|
|
#if defined(VERBOSE) && defined(DBG_UTIL)
|
|
renderFPSCounter();
|
|
renderMemAvailable();
|
|
#endif
|
|
VERBOSE_TRACE( "Device::flip(): Using true page flipping" );
|
|
|
|
// use true page flipping. Hopefully, the 3D hardware
|
|
// is flushed on this flip call (rumours have it that
|
|
// way), otherwise, perform the Lock hack as for the
|
|
// Blt below.
|
|
if( SUCCEEDED(mpPrimarySurface->Flip( NULL, DDFLIP_WAIT )) )
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
VERBOSE_TRACE( "Device::flip(): Using blt for page flipping" );
|
|
|
|
// determine actual window position
|
|
::basegfx::B2IPoint aDestPoint( rUpdateArea.getMinimum() );
|
|
::basegfx::B2IRange aSourceArea( rUpdateArea );
|
|
::basegfx::B2IRange aDestArea( 0,0,
|
|
static_cast<sal_Int32>(rCurrWindowArea.getWidth()),
|
|
static_cast<sal_Int32>(rCurrWindowArea.getHeight()) );
|
|
convert2Screen( aDestPoint, aDestArea );
|
|
|
|
// perform clipping
|
|
if( !::canvas::tools::clipBlit( aSourceArea,
|
|
aDestPoint,
|
|
rUpdateArea,
|
|
aDestArea ) )
|
|
return true; // fully clipped, but still, in a way,
|
|
// successful.
|
|
|
|
// TODO(P1): Rumours have it that the 3D hardware
|
|
// _might_ still be rendering with flaky drivers,
|
|
// which don't flush properly on Blt(). It was said,
|
|
// that 'usually', it works to lock the 3D render
|
|
// target (the backbuffer in this case). OTOH, I've
|
|
// found that this tends to degrade performance
|
|
// significantly on complying cards...
|
|
|
|
// TODO(P1): Up until rev. 1.3, this method contained
|
|
// code to make sure the blit will start _immediately_
|
|
// after the Blt call. If this is not warranted, wait
|
|
// for the next vsync. As this case was found to be
|
|
// extremely seldom, kicked out (what's more, there's
|
|
// simply no guarantee that the blitter will be
|
|
// available at any point in the code - Windows still
|
|
// is a preemptive multi-processing environment. And
|
|
// _if_ we're competing with someone over the blitter,
|
|
// we will do so the next VBLANK interval, and the
|
|
// following...)
|
|
|
|
// screen update seems to be smoother when waiting
|
|
// for vblank in every case - even when blitter
|
|
// supports the DDBLTFX_NOTEARING flag.
|
|
if( FAILED(mpDirectDraw->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,
|
|
NULL)) )
|
|
return false;
|
|
|
|
DDBLTFX aBltFx;
|
|
DDBLTFX* pBltFX = NULL;
|
|
if( mbHasNoTearingBlt )
|
|
{
|
|
// Blt can internally schedule for no-tearing
|
|
// ===========================================
|
|
|
|
rtl_fillMemory( &aBltFx,
|
|
sizeof(aBltFx), 0 );
|
|
aBltFx.dwSize = sizeof(aBltFx);
|
|
aBltFx.dwDDFX = DDBLTFX_NOTEARING;
|
|
|
|
pBltFX = &aBltFx;
|
|
}
|
|
|
|
if( doBlit( aDestPoint,
|
|
*mpPrimarySurface,
|
|
aSourceArea,
|
|
*mpBackBufferSurface,
|
|
pBltFX,false ) )
|
|
{
|
|
#if defined(VERBOSE) && defined(DBG_UTIL)
|
|
renderFPSCounter();
|
|
renderMemAvailable();
|
|
#endif
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::disposing
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DXRenderModule::disposing()
|
|
{
|
|
if(!(mhWnd))
|
|
return;
|
|
|
|
mpTexture.reset();
|
|
mpWindow.reset();
|
|
mhWnd=NULL;
|
|
|
|
// refrain from releasing the DX5 objects - deleting the
|
|
// DX5 device seems to kill the whole engine, including
|
|
// all objects we might still hold references to
|
|
// (surfaces, e.g.)
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::screenshot
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DXRenderModule::screenShot()
|
|
{
|
|
if(!(mpBackBufferSurface.get()))
|
|
return;
|
|
char filename[256];
|
|
static sal_uInt32 counter = 0;
|
|
sprintf(filename,"c:\\shot%d.bmp",counter++);
|
|
dumpSurface(mpBackBufferSurface,filename);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::validateMainSurfaces
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool DXRenderModule::validateMainSurfaces()
|
|
{
|
|
if(mpPrimarySurface.get()) {
|
|
if(mpPrimarySurface->IsLost() == DDERR_SURFACELOST) {
|
|
if(FAILED(mpPrimarySurface->Restore()))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(mpBackBufferSurface.get()) {
|
|
if(mpBackBufferSurface->IsLost() == DDERR_SURFACELOST)
|
|
{
|
|
// TODO(F1): simply restoring the backbuffer does not
|
|
// work as expected, we need to re-create everything
|
|
// from scratch. find out why...
|
|
//if(SUCCEEDED(mpBackBufferSurface->Restore()))
|
|
// return setup3DDevice();
|
|
|
|
mpBackBufferSurface.reset();
|
|
|
|
// get us a backbuffer for simulated flipping
|
|
IDirectDrawSurface* pSurface;
|
|
|
|
// TODO(P2): 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. YMMV.
|
|
const ::basegfx::B2ISize aSize( getFramebufferSize() );
|
|
|
|
DDSURFACEDESC aSurfaceDesc;
|
|
rtl_fillMemory( &aSurfaceDesc, sizeof(DDSURFACEDESC), 0 );
|
|
aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
|
|
aSurfaceDesc.dwHeight= aSize.getY();
|
|
aSurfaceDesc.dwWidth = aSize.getX();
|
|
|
|
aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
|
|
|
|
HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);
|
|
|
|
if( FAILED( nRes ) )
|
|
{
|
|
if( nRes == DDERR_OUTOFVIDEOMEMORY )
|
|
{
|
|
// local vid mem failed. Maybe AGP mem works?
|
|
aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
|
|
if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL)) )
|
|
{
|
|
// no chance
|
|
return false;
|
|
}
|
|
|
|
VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer reverted to non-local video mem" );
|
|
}
|
|
else
|
|
{
|
|
// no chance
|
|
VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
VERBOSE_TRACE( "Device::Device(): created backbuffer of size %d times %d pixel",
|
|
aSurfaceDesc.dwWidth,
|
|
aSurfaceDesc.dwHeight );
|
|
|
|
mpBackBufferSurface = COMReference< IDirectDrawSurface >(pSurface);
|
|
|
|
return setup3DDevice();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DXRenderModule::renderInfoText( const ::rtl::OUString& rStr,
|
|
const Gdiplus::PointF& rPos ) const
|
|
{
|
|
ENSURE_OR_THROW( !mnBeginSceneCount,
|
|
"Device::renderInfoText(): within 3D scene" );
|
|
|
|
// render text directly to primary surface
|
|
GraphicsSharedPtr pGraphics;
|
|
|
|
if( mbPageFlipping )
|
|
{
|
|
// render on top of backbuffer. We have
|
|
// page flipping, anyway, thus this will
|
|
// cost us nothing.
|
|
pGraphics = createSurfaceGraphics( mpBackBufferSurface );
|
|
}
|
|
else
|
|
{
|
|
// render FPS directly to front buffer.
|
|
// That saves us another explicit blit,
|
|
// and for me, the FPS counter can blink,
|
|
// if it likes to...
|
|
pGraphics = createSurfaceGraphics( mpPrimarySurface );
|
|
}
|
|
|
|
if( !mbPageFlipping )
|
|
{
|
|
// clear background. We might be doing optimized redraws,
|
|
// and the background under the FPS count will then not be
|
|
// cleared.
|
|
Gdiplus::SolidBrush aBrush(
|
|
Gdiplus::Color( 255, 255, 255 ) );
|
|
|
|
pGraphics->FillRectangle( &aBrush,
|
|
rPos.X, rPos.Y, 80.0, 20.0 );
|
|
}
|
|
|
|
Gdiplus::SolidBrush aBrush(
|
|
Gdiplus::Color( 255, 0, 255 ) );
|
|
Gdiplus::Font aFont( NULL,
|
|
16,
|
|
Gdiplus::FontStyleRegular,
|
|
Gdiplus::UnitWorld,
|
|
NULL );
|
|
pGraphics->DrawString( reinterpret_cast<LPCWSTR>(rStr.getStr()),
|
|
rStr.getLength(),
|
|
&aFont,
|
|
rPos,
|
|
&aBrush );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::renderMemAvailable
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DXRenderModule::renderMemAvailable() const
|
|
{
|
|
ENSURE_OR_THROW( !mnBeginSceneCount,
|
|
"DXRenderModule::renderMemAvailable(): within 3D scene" );
|
|
|
|
const double nSurfaceMem( getAvailableSurfaceMem()/1024 );
|
|
|
|
::rtl::OUString text( ::rtl::math::doubleToUString( nSurfaceMem,
|
|
rtl_math_StringFormat_F,
|
|
2,'.',NULL,' ') );
|
|
|
|
// pad with leading space
|
|
while( text.getLength() < 6 )
|
|
text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;
|
|
|
|
text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("S: ")) + text;
|
|
|
|
renderInfoText( text,
|
|
Gdiplus::PointF( 0.0, 20) );
|
|
|
|
|
|
const double nTexMem( getAvailableTextureMem()/1024 );
|
|
|
|
text = ::rtl::math::doubleToUString( nTexMem,
|
|
rtl_math_StringFormat_F,
|
|
2,'.',NULL,' ');
|
|
// pad with leading space
|
|
while( text.getLength() < 6 )
|
|
text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;
|
|
|
|
text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("T: ")) + text;
|
|
|
|
renderInfoText( text,
|
|
Gdiplus::PointF( 0.0, 40) );
|
|
|
|
VERBOSE_TRACE( "dxcanvas: %f free surface mem, %f free texture mem",
|
|
nSurfaceMem, nTexMem );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::renderFPSCounter
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DXRenderModule::renderFPSCounter() const
|
|
{
|
|
ENSURE_OR_THROW( !mnBeginSceneCount,
|
|
"DXRenderModule::ren derFPSCounter(): within 3D scene" );
|
|
|
|
const double denominator( maLastUpdate.getElapsedTime() );
|
|
maLastUpdate.reset();
|
|
|
|
::rtl::OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator,
|
|
rtl_math_StringFormat_F,
|
|
2,'.',NULL,' ') );
|
|
|
|
// pad with leading space
|
|
while( text.getLength() < 6 )
|
|
text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;
|
|
|
|
text += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" fps"));
|
|
|
|
renderInfoText( text,
|
|
Gdiplus::PointF() );
|
|
|
|
VERBOSE_TRACE( "dxcanvas: %f FPS",
|
|
denominator == 0.0 ? 100.0 : 1.0/denominator );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::resize
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DXRenderModule::resize( const ::basegfx::B2IRange& rect )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
if( mhWnd==0 )
|
|
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());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// DXRenderModule::getPageSize
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
::basegfx::B2IVector DXRenderModule::getPageSize()
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
return maPageSize;
|
|
}
|
|
|
|
::canvas::ISurfaceSharedPtr DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
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) );
|
|
}
|
|
|
|
void DXRenderModule::beginPrimitive( PrimitiveType eType )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
ENSURE_OR_THROW( !mnBeginSceneCount,
|
|
"DXRenderModule::beginPrimitive(): nested call" );
|
|
|
|
++mnBeginSceneCount;
|
|
meType=eType;
|
|
mnCount=0;
|
|
}
|
|
|
|
void DXRenderModule::endPrimitive()
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
--mnBeginSceneCount;
|
|
meType=PRIMITIVE_TYPE_UNKNOWN;
|
|
mnCount=0;
|
|
}
|
|
|
|
void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex )
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
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 types" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool DXRenderModule::isError()
|
|
{
|
|
// TODO(P2): get rid of those fine-grained locking
|
|
::osl::MutexGuard aGuard( maMutex );
|
|
|
|
return mbError;
|
|
}
|
|
|
|
void DXRenderModule::flushVertexCache()
|
|
{
|
|
if(!(maVertexCache.size()))
|
|
return;
|
|
|
|
mbError=true;
|
|
|
|
if( FAILED(mpDirect3DDevice->BeginScene()) )
|
|
return;
|
|
|
|
// enable texture alpha blending
|
|
if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE)))
|
|
return;
|
|
|
|
// enable texture alpha modulation, for honoring fAlpha
|
|
if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND,
|
|
D3DTBLEND_MODULATEALPHA)) )
|
|
return;
|
|
|
|
// enable texture magnification filtering (don't care if this
|
|
// fails, it's just visually more pleasant)
|
|
mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAG,
|
|
D3DFILTER_LINEAR);
|
|
|
|
// enable texture minification filtering (don't care if this
|
|
// fails, it's just visually more pleasant)
|
|
mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMIN,
|
|
D3DFILTER_LINEAR);
|
|
|
|
// enable subpixel texture output (don't care if this
|
|
// fails, it's just visually more pleasant)
|
|
mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_SUBPIXEL,
|
|
TRUE);
|
|
|
|
// normal combination of object...
|
|
if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,
|
|
D3DBLEND_SRCALPHA)) )
|
|
return;
|
|
|
|
// ..and background color
|
|
if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_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(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_CULLMODE,
|
|
D3DCULL_NONE)) )
|
|
return;
|
|
|
|
mbError=false;
|
|
|
|
const float nHalfPixelSizeX(0.5f/maPageSize.getX());
|
|
const float nHalfPixelSizeY(0.5f/maPageSize.getY());
|
|
sal_uInt32 nIndex(0);
|
|
const std::size_t size(maVertexCache.size());
|
|
D3DTLVERTEX *vertices = static_cast<D3DTLVERTEX *>(_alloca(sizeof(D3DTLVERTEX)*size));
|
|
vertexCache_t::const_iterator it(maVertexCache.begin());
|
|
while(it != maVertexCache.end())
|
|
{
|
|
vertices[nIndex++] = D3DTLVERTEX(
|
|
D3DVECTOR(static_cast<D3DVALUE>(it->x),
|
|
static_cast<D3DVALUE>(it->y),
|
|
static_cast<D3DVALUE>(it->z)),
|
|
1,
|
|
D3DRGBA(1,1,1,it->a),
|
|
D3DRGBA(0,0,0,0),
|
|
static_cast<float>(it->u + nHalfPixelSizeX),
|
|
static_cast<float>(it->v + nHalfPixelSizeY));
|
|
++it;
|
|
}
|
|
|
|
maVertexCache.clear();
|
|
|
|
mbError |= FAILED(mpDirect3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST,
|
|
D3DVT_TLVERTEX,
|
|
(LPVOID)vertices,
|
|
size,
|
|
0));
|
|
|
|
mbError |= FAILED(mpDirect3DDevice->EndScene());
|
|
}
|
|
}
|
|
|
|
IDXRenderModuleSharedPtr createRenderModule( const ::Window& rParent )
|
|
{
|
|
return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) );
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|