1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_canvas.hxx" 30 31 #if DIRECTX_VERSION < 0x0900 32 33 // Nvidia GeForce Go 6800 crashes with a bluescreen if we take the 34 // maximum texture size, which would be twice as large. this behaviors 35 // has only been observed on directx5. 36 // This value is simply the maximum size for textures we request from 37 // the system, it has absolutely nothing to do with the size of primitives 38 // we're able to render, both concepts are totally independent from each other. 39 #define MAX_TEXTURE_SIZE (2048) 40 #define MIN_TEXTURE_SIZE (32) 41 //#define FAKE_MAX_NUMBER_TEXTURES (2) 42 //#define FAKE_MAX_TEXTURE_SIZE (512) 43 44 ////////////////////////////////////////////////////////////////////////////////// 45 // includes 46 ////////////////////////////////////////////////////////////////////////////////// 47 #include <vcl/syschild.hxx> 48 #include <vcl/window.hxx> 49 #include <canvas/debug.hxx> 50 #include <canvas/verbosetrace.hxx> 51 #include <canvas/elapsedtime.hxx> 52 #include <canvas/canvastools.hxx> 53 #include <canvas/rendering/icolorbuffer.hxx> 54 #include <canvas/rendering/isurface.hxx> 55 #include <canvas/rendering/irendermodule.hxx> 56 #include <tools/diagnose_ex.h> 57 #include <basegfx/numeric/ftools.hxx> 58 #include <basegfx/vector/b2dsize.hxx> 59 #include <basegfx/vector/b2isize.hxx> 60 #include <basegfx/point/b2ipoint.hxx> 61 #include <basegfx/range/b2irectangle.hxx> 62 #include <boost/scoped_ptr.hpp> 63 #include <com/sun/star/lang/NoSupportException.hpp> 64 65 #define COMPILE_MULTIMON_STUBS 66 67 #include "dx_rendermodule.hxx" 68 #include "dx_surfacegraphics.hxx" 69 #include <vcl/sysdata.hxx> 70 71 #undef WB_LEFT 72 #undef WB_RIGHT 73 74 #include "dx_impltools.hxx" 75 #include <malloc.h> 76 77 #if defined(DX_DEBUG_IMAGES) 78 # if OSL_DEBUG_LEVEL > 0 79 # include <imdebug.h> 80 # undef min 81 # undef max 82 # endif 83 #endif 84 85 #undef COMPILE_MULTIMON_STUBS 86 87 #include <stdio.h> 88 89 #define MONITOR_DEFAULTTONULL 0x00000000 90 #define MONITOR_DEFAULTTOPRIMARY 0x00000001 91 #define MONITOR_DEFAULTTONEAREST 0x00000002 92 93 using namespace ::com::sun::star; 94 95 ////////////////////////////////////////////////////////////////////////////////// 96 // 'dxcanvas' namespace 97 ////////////////////////////////////////////////////////////////////////////////// 98 99 namespace dxcanvas 100 { 101 namespace 102 { 103 bool doBlit( const ::basegfx::B2IPoint& rDestPos, 104 IDirectDrawSurface& rOutSurface, 105 const ::basegfx::B2IRange& rSourceArea, 106 IDirectDrawSurface& rSourceSurface, 107 DDBLTFX* pBltFx, 108 bool bForceSoftware ) 109 { 110 if( !bForceSoftware ) 111 { 112 // blit surface to backbuffer 113 RECT aOutRect = 114 { 115 rDestPos.getX(), 116 rDestPos.getY(), 117 rDestPos.getX() + static_cast<sal_Int32>(rSourceArea.getWidth()), 118 rDestPos.getY() + static_cast<sal_Int32>(rSourceArea.getHeight()), 119 }; 120 RECT aSourceRect = 121 { 122 rSourceArea.getMinX(), 123 rSourceArea.getMinY(), 124 rSourceArea.getMaxX(), 125 rSourceArea.getMaxY() 126 }; 127 128 if( SUCCEEDED(rOutSurface.Blt( &aOutRect, 129 &rSourceSurface, 130 &aSourceRect, 131 DDBLT_WAIT, 132 pBltFx )) ) 133 { 134 return true; 135 } 136 } 137 138 // failed, or forced to use SW copy. attempt manual copy. 139 bool bResult = false; 140 141 // lock source surface 142 DDSURFACEDESC aDescSrc; 143 rtl_fillMemory(&aDescSrc,sizeof(DDSURFACEDESC),0); 144 aDescSrc.dwSize = sizeof(DDSURFACEDESC); 145 const DWORD dwSrcFlags = DDLOCK_NOSYSLOCK| 146 DDLOCK_SURFACEMEMORYPTR| 147 DDLOCK_WAIT| 148 DDLOCK_READONLY; 149 if(SUCCEEDED(rSourceSurface.Lock(NULL, 150 &aDescSrc, 151 dwSrcFlags, 152 NULL))) 153 { 154 // lock destination surface 155 DDSURFACEDESC aDescDst; 156 rtl_fillMemory(&aDescDst,sizeof(DDSURFACEDESC),0); 157 aDescDst.dwSize = sizeof(DDSURFACEDESC); 158 const DWORD dwDstFlags = DDLOCK_NOSYSLOCK| 159 DDLOCK_SURFACEMEMORYPTR| 160 DDLOCK_WAIT| 161 DDLOCK_WRITEONLY; 162 if(SUCCEEDED(rOutSurface.Lock(NULL, 163 &aDescDst, 164 dwDstFlags, 165 NULL))) 166 { 167 sal_uInt32 nSrcFormat; 168 nSrcFormat = ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwRGBAlphaBitMask)<<12; 169 nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwRBitMask)<<8; 170 nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwGBitMask)<<4; 171 nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwBBitMask); 172 173 sal_uInt32 nDstFormat; 174 nDstFormat = ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwRGBAlphaBitMask)<<12; 175 nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwRBitMask)<<8; 176 nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwGBitMask)<<4; 177 nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwBBitMask); 178 179 // TODO(E1): Use numeric_cast to catch overflow here 180 const sal_uInt32 nWidth( static_cast<sal_uInt32>( 181 rSourceArea.getWidth() ) ); 182 const sal_uInt32 nHeight( static_cast<sal_uInt32>( 183 rSourceArea.getHeight() ) ); 184 185 if((nSrcFormat == 0x8888) && (nDstFormat == 0x0565)) 186 { 187 // medium range 8888 to 0565 pixel format conversion. 188 bResult = true; 189 sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface + 190 rSourceArea.getMinY()*aDescSrc.lPitch + 191 (rSourceArea.getMinX()<<2); 192 sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface + 193 rDestPos.getY()*aDescDst.lPitch + 194 (rDestPos.getX()<<1); 195 for(sal_uInt32 y=0; y<nHeight; ++y) 196 { 197 sal_uInt32 *pSrcScanline = (sal_uInt32 *)pSrcSurface; 198 sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface; 199 for(sal_uInt32 x=0; x<nWidth; ++x) 200 { 201 sal_uInt32 srcPixel = *pSrcScanline++; 202 sal_uInt16 dstPixel; 203 dstPixel = (sal_uInt16)((srcPixel & 0x0000F8) >> 3); 204 dstPixel |= (srcPixel & 0x00FC00) >> 5; 205 dstPixel |= (srcPixel & 0xF80000) >> 8; 206 *pDstScanline++ = dstPixel; 207 } 208 pSrcSurface += aDescSrc.lPitch; 209 pDstSurface += aDescDst.lPitch; 210 } 211 } 212 else if((nSrcFormat == 0x8888) && (nDstFormat == 0x0888)) 213 { 214 // medium range 8888 to 0888 pixel format conversion. 215 bResult = true; 216 sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface + 217 rSourceArea.getMinY()*aDescSrc.lPitch + 218 (rSourceArea.getMinX()<<2); 219 sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface + 220 rDestPos.getY()*aDescDst.lPitch + 221 (rDestPos.getX()<<2); 222 for(sal_uInt32 y=0; y<nHeight; ++y) 223 { 224 sal_uInt32 *pSrcScanline = (sal_uInt32 *)pSrcSurface; 225 sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface; 226 for(sal_uInt32 x=0; x<nWidth; ++x) 227 { 228 *pDstScanline++ = (sal_uInt16)*pSrcScanline++; 229 } 230 pSrcSurface += aDescSrc.lPitch; 231 pDstSurface += aDescDst.lPitch; 232 } 233 } 234 else if((nSrcFormat == 0x8888) && (nDstFormat == 0x1555)) 235 { 236 // medium range 8888 to 1555 pixel format conversion. 237 bResult = true; 238 sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface + 239 rSourceArea.getMinY()*aDescSrc.lPitch + 240 (rSourceArea.getMinX()<<2); 241 sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface + 242 rDestPos.getY()*aDescDst.lPitch + 243 (rDestPos.getX()<<1); 244 for(sal_uInt32 y=0; y<nHeight; ++y) 245 { 246 sal_uInt32 *pSrcScanline = (sal_uInt32*)pSrcSurface; 247 sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface; 248 for(sal_uInt32 x=0; x<nWidth; ++x) 249 { 250 sal_uInt32 srcPixel = *pSrcScanline++; 251 sal_uInt16 dstPixel; 252 dstPixel = (sal_uInt16)((srcPixel & 0x000000F8) >> 3); 253 dstPixel |= (srcPixel & 0x0000F800) >> 6; 254 dstPixel |= (srcPixel & 0x00F80000) >> 9; 255 dstPixel |= (srcPixel & 0x80000000) >> 16; 256 *pDstScanline++ = dstPixel; 257 } 258 pSrcSurface += aDescSrc.lPitch; 259 pDstSurface += aDescDst.lPitch; 260 } 261 } 262 263 // unlock destination surface 264 rOutSurface.Unlock(NULL); 265 } 266 267 // unlock source surface 268 rSourceSurface.Unlock(NULL); 269 } 270 271 return bResult; 272 } 273 274 void dumpSurface( const COMReference<IDirectDrawSurface> &pSurface, const char *szFilename ) 275 { 276 if(!(pSurface.get())) 277 return; 278 279 DDSURFACEDESC aSurfaceDesc; 280 rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 ); 281 aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); 282 283 if( FAILED(pSurface->Lock( NULL, 284 &aSurfaceDesc, 285 DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY, 286 NULL)) ) 287 return; 288 289 const std::size_t dwBitmapSize(aSurfaceDesc.dwWidth*aSurfaceDesc.dwHeight*4); 290 sal_uInt8 *pBuffer = static_cast<sal_uInt8 *>(_alloca(dwBitmapSize)); 291 if(pBuffer) 292 { 293 sal_uInt8 *pSource = reinterpret_cast<sal_uInt8 *>(aSurfaceDesc.lpSurface); 294 sal_uInt8 *pDest = reinterpret_cast<sal_uInt8 *>(pBuffer); 295 const std::size_t dwDestPitch(aSurfaceDesc.dwWidth<<2); 296 pDest += aSurfaceDesc.dwHeight*dwDestPitch; 297 for(sal_uInt32 y=0; y<aSurfaceDesc.dwHeight; ++y) 298 { 299 pDest -= dwDestPitch; 300 rtl_copyMemory( pDest, pSource, dwDestPitch ); 301 pSource += aSurfaceDesc.lPitch; 302 } 303 304 if(FILE *fp = fopen(szFilename,"wb")) 305 { 306 BITMAPINFOHEADER bitmapInfo; 307 308 bitmapInfo.biSize = sizeof(BITMAPINFOHEADER); 309 bitmapInfo.biWidth = aSurfaceDesc.dwWidth; 310 bitmapInfo.biHeight = aSurfaceDesc.dwHeight; 311 bitmapInfo.biPlanes = 1; 312 bitmapInfo.biBitCount = 32; 313 bitmapInfo.biCompression = BI_RGB; 314 bitmapInfo.biSizeImage = 0; 315 bitmapInfo.biXPelsPerMeter = 0; 316 bitmapInfo.biYPelsPerMeter = 0; 317 bitmapInfo.biClrUsed = 0; 318 bitmapInfo.biClrImportant = 0; 319 320 const std::size_t dwFileSize(sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwBitmapSize); 321 322 BITMAPFILEHEADER header; 323 header.bfType = 'MB'; 324 header.bfSize = dwFileSize; 325 header.bfReserved1 = 0; 326 header.bfReserved2 = 0; 327 header.bfOffBits = sizeof(BITMAPFILEHEADER) + bitmapInfo.biSize; 328 329 fwrite(&header,1,sizeof(BITMAPFILEHEADER),fp); 330 fwrite(&bitmapInfo,1,sizeof(BITMAPINFOHEADER),fp); 331 fwrite(pBuffer,1,dwBitmapSize,fp); 332 333 fclose(fp); 334 } 335 } 336 337 pSurface->Unlock(NULL); 338 } 339 340 void clearSurface( const COMReference<IDirectDrawSurface>& pSurface ) 341 { 342 if(!(pSurface.is())) 343 return; 344 345 DDBLTFX aBltFx; 346 347 rtl_fillMemory( &aBltFx, 348 sizeof(DDBLTFX), 0 ); 349 aBltFx.dwSize = sizeof(DDBLTFX); 350 aBltFx.dwFillColor = 0; 351 352 pSurface->Blt( NULL, 353 NULL, 354 NULL, 355 DDBLT_COLORFILL | DDBLT_WAIT, 356 &aBltFx ); 357 } 358 359 // Define struct for MonitorEntry 360 struct MonitorEntry 361 { 362 GUID mnGUID; 363 HMONITOR mhMonitor; 364 MONITORINFO maMonitorInfo; 365 }; 366 367 // define type for MonitorList 368 typedef ::std::vector< MonitorEntry > MonitorList; 369 370 // Win32 system callback for DirectDrawEnumerateExA call 371 BOOL WINAPI EnumerateExA_Callback( GUID FAR* lpGUID, 372 LPSTR /*lpDriverDescription*/, 373 LPSTR /*lpDriverName*/, 374 LPVOID lpContext, 375 HMONITOR hMonitor ) 376 { 377 if(lpGUID) 378 { 379 MonitorList* pMonitorList = (MonitorList*)lpContext; 380 MonitorEntry aEntry; 381 382 aEntry.mnGUID = *lpGUID; 383 aEntry.mhMonitor = hMonitor; 384 aEntry.maMonitorInfo.cbSize = sizeof(MONITORINFO); 385 GetMonitorInfo( hMonitor, 386 &aEntry.maMonitorInfo ); 387 388 pMonitorList->push_back(aEntry); 389 } 390 391 return DDENUMRET_OK; 392 } 393 394 void fillMonitorList( MonitorList& rMonitorList ) 395 { 396 // Try to fill MonitorList. If neither lib or call to 397 // DirectDrawEnumerateExA does not exist, it's an old 398 // DX version (< 5.0), or system does not support 399 // multiple monitors. 400 HINSTANCE hInstance = LoadLibrary("ddraw.dll"); 401 402 if(hInstance) 403 { 404 LPDIRECTDRAWENUMERATEEX lpDDEnumEx = 405 (LPDIRECTDRAWENUMERATEEX)GetProcAddress(hInstance,"DirectDrawEnumerateExA"); 406 407 if(lpDDEnumEx) 408 lpDDEnumEx( (LPDDENUMCALLBACKEXA) EnumerateExA_Callback, 409 &rMonitorList, 410 DDENUM_ATTACHEDSECONDARYDEVICES ); 411 412 FreeLibrary(hInstance); 413 } 414 } 415 416 IDirectDraw2* createDirectDraw( const MonitorList& rMonitorList, 417 MONITORINFO& rMonitorInfo, 418 HWND renderWindow ) 419 { 420 GUID* gpSelectedDriverGUID = NULL; 421 422 // if we have multiple monitors, choose a gpSelectedDriverGUID from monitor list 423 HMONITOR hMonitor = MonitorFromWindow(renderWindow, 424 MONITOR_DEFAULTTONEAREST); 425 426 MonitorList::const_iterator aCurr = rMonitorList.begin(); 427 const MonitorList::const_iterator aEnd = rMonitorList.end(); 428 while( !gpSelectedDriverGUID && aCurr != aEnd ) 429 { 430 if(hMonitor == aCurr->mhMonitor) 431 { 432 // This is the monitor we are running on 433 gpSelectedDriverGUID = const_cast<GUID*>(&aCurr->mnGUID); 434 rMonitorInfo = aCurr->maMonitorInfo; 435 } 436 437 ++aCurr; 438 } 439 440 IDirectDraw* pDirectDraw; 441 if( FAILED( DirectDrawCreate( gpSelectedDriverGUID, &pDirectDraw, NULL ))) 442 return NULL; 443 444 IDirectDraw2* pDirectDraw2; 445 if( FAILED( pDirectDraw->QueryInterface( IID_IDirectDraw2, (LPVOID*)&pDirectDraw2 ))) 446 return NULL; 447 448 // queryInterface bumped up the refcount, so release the 449 // reference to the original IDirectDraw interface. 450 pDirectDraw->Release(); 451 452 return pDirectDraw2; 453 } 454 455 HRESULT WINAPI EnumTextureFormatsCallback( LPDDSURFACEDESC pSurfaceDesc, 456 LPVOID pContext ) 457 { 458 // dirty cast of given context back to result ModeSelectContext 459 DDPIXELFORMAT* pResult = (DDPIXELFORMAT*)pContext; 460 461 if( pResult == NULL || pSurfaceDesc == NULL ) 462 return DDENUMRET_CANCEL; 463 464 VERBOSE_TRACE( "EnumTextureFormatsCallback: advertised texture format has dwRGBBitCount %d, dwRBitMask %x, " 465 "dwGBitMask %x, dwBBitMask %x and dwRGBAlphaBitMask %x. The format uses %s alpha.", 466 pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount, 467 pSurfaceDesc->ddpfPixelFormat.dwRBitMask, 468 pSurfaceDesc->ddpfPixelFormat.dwGBitMask, 469 pSurfaceDesc->ddpfPixelFormat.dwBBitMask, 470 pSurfaceDesc->ddpfPixelFormat.dwRGBAlphaBitMask, 471 pSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPREMULT ? "premultiplied" : "non-premultiplied" ); 472 473 // Only accept RGB surfaces with alpha channel 474 if( (DDPF_ALPHAPIXELS | DDPF_RGB) == 475 (pSurfaceDesc->ddpfPixelFormat.dwFlags & (DDPF_ALPHAPIXELS | DDPF_RGB)) ) 476 { 477 // ignore formats with the DDPF_ALPHAPREMULT flag 478 if(!(pSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPREMULT)) 479 { 480 // take widest alpha channel available 481 if( pSurfaceDesc->ddpfPixelFormat.dwAlphaBitDepth > pResult->dwAlphaBitDepth ) 482 { 483 // take new format 484 rtl_copyMemory( pResult, &pSurfaceDesc->ddpfPixelFormat, sizeof(DDPIXELFORMAT) ); 485 } 486 else if( pSurfaceDesc->ddpfPixelFormat.dwAlphaBitDepth == pResult->dwAlphaBitDepth ) 487 { 488 // tie-breaking: take highest bitcount 489 if( pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount > pResult->dwRGBBitCount ) 490 { 491 // take new format 492 rtl_copyMemory( pResult, &pSurfaceDesc->ddpfPixelFormat, sizeof(DDPIXELFORMAT) ); 493 } 494 } 495 } 496 } 497 498 return DDENUMRET_OK; 499 } 500 501 class DXRenderModule; 502 503 ////////////////////////////////////////////////////////////////////////////////// 504 // DXSurface 505 ////////////////////////////////////////////////////////////////////////////////// 506 507 /** ISurface implemenation. 508 509 @attention holds the DXRenderModule via non-refcounted 510 reference! This is safe with current state of affairs, since 511 the canvas::PageManager holds surface and render module via 512 shared_ptr (and makes sure all surfaces are deleted before its 513 render module member goes out of scope). 514 */ 515 class DXSurface : public canvas::ISurface 516 { 517 public: 518 DXSurface( DXRenderModule& rRenderModule, 519 const ::basegfx::B2ISize& rSize ); 520 ~DXSurface(); 521 522 virtual bool selectTexture(); 523 virtual bool isValid(); 524 virtual bool update( const ::basegfx::B2IPoint& rDestPos, 525 const ::basegfx::B2IRange& rSourceRect, 526 ::canvas::IColorBuffer& rSource ); 527 virtual ::basegfx::B2IVector getSize(); 528 529 private: 530 /// Guard local methods against concurrent acces to RenderModule 531 class ImplRenderModuleGuard : private ::boost::noncopyable 532 { 533 public: 534 explicit inline ImplRenderModuleGuard( DXRenderModule& rRenderModule ); 535 inline ~ImplRenderModuleGuard(); 536 537 private: 538 DXRenderModule& mrRenderModule; 539 }; 540 541 DXRenderModule& mrRenderModule; 542 543 COMReference<IDirectDrawSurface> mpSurface; 544 COMReference<IDirect3DTexture2> mpTexture; 545 546 ::basegfx::B2IVector maSize; 547 }; 548 549 ////////////////////////////////////////////////////////////////////////////////// 550 // DXRenderModule 551 ////////////////////////////////////////////////////////////////////////////////// 552 553 /// Default implementation of IDXRenderModule 554 class DXRenderModule : public IDXRenderModule 555 { 556 public: 557 explicit DXRenderModule( const ::Window& rWindow ); 558 559 virtual void lock() const { maMutex.acquire(); } 560 virtual void unlock() const { maMutex.release(); } 561 562 virtual COMReference<IDirectDrawSurface> 563 createSystemMemorySurface( const ::basegfx::B2IVector& rSize ); 564 565 virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea, 566 const ::basegfx::B2IRectangle& rCurrWindowArea ); 567 568 virtual void resize( const ::basegfx::B2IRange& rect ); 569 virtual HWND getHWND() const { return mhWnd; } 570 virtual void disposing(); 571 virtual void screenShot(); 572 virtual ::basegfx::B2IVector getPageSize(); 573 virtual ::canvas::ISurfaceSharedPtr createSurface( const ::basegfx::B2IVector& surfaceSize ); 574 virtual void beginPrimitive( PrimitiveType eType ); 575 virtual void endPrimitive(); 576 virtual void pushVertex( const ::canvas::Vertex& vertex ); 577 virtual bool isError(); 578 579 const D3DDEVICEDESC& getDeviceDesc() const { return maDeviceDesc; } 580 const DDPIXELFORMAT& getTextureFormat() const { return maTextureFormat; } 581 COMReference<IDirectDraw2> getDirectDraw() { return mpDirectDraw; } 582 COMReference< IDirect3DDevice2 > getDevice() { return mpDirect3DDevice; } 583 584 void flushVertexCache(); 585 586 struct ModeSelectContext 587 { 588 DDSURFACEDESC selectedDesc; 589 ::basegfx::B2ISize requestedSize; 590 }; 591 592 /** Query actual size of the device 593 594 This is especially interesting for fullscreen devices 595 */ 596 ::basegfx::B2ISize getFramebufferSize() const; 597 598 /** Query the amount of memory available for new surfaces 599 600 This might differ from getAvailableTextureMem() 601 @see getAvailableTextureMem() 602 603 @param bWithAGPMema 604 When true, returned value includes non-local, 605 i.e. AGP-able memory, too. 606 607 @return the amount of free surface mem 608 */ 609 std::size_t getAvailableSurfaceMem( bool bWithAGPMem=true ) const; 610 611 /** Query the amount of memory available for new textures 612 613 This might differ from getAvailableSurfaceMem() 614 @see getAvailableSurfaceMem() 615 616 @param bWithAGPMema 617 When true, returned value includes non-local, 618 i.e. AGP-able memory, too. 619 620 @return the amount of free texture mem 621 */ 622 std::size_t getAvailableTextureMem( bool bWithAGPMem=true ) const; 623 624 private: 625 bool queryCaps(); 626 bool validateCaps(); 627 bool setup3DDevice(); 628 unsigned int getDisplayFormat() const; 629 630 void convert2Screen( ::basegfx::B2IPoint& io_rDestPos, 631 ::basegfx::B2IRange& io_rDestArea ); 632 633 void renderInfoText( const ::rtl::OUString& rStr, 634 const Gdiplus::PointF& rPos ) const; 635 void renderFPSCounter() const; 636 void renderMemAvailable() const; 637 638 bool create( const ::Window& rWindow ); 639 bool validateMainSurfaces(); 640 641 /** This object represents the DirectX state machine. In order 642 to serialize access to DirectX's global state, a global 643 mutex is required. 644 */ 645 static ::osl::Mutex maMutex; 646 647 HWND mhWnd; 648 ::boost::scoped_ptr<SystemChildWindow> mpWindow; 649 ::basegfx::B2IVector maSize; 650 651 ModeSelectContext maSelectedFullscreenMode; 652 DDPIXELFORMAT maTextureFormat; 653 654 MONITORINFO maMonitorInfo; // monitor info for mpDirectDraw's monitor 655 COMReference<IDirectDraw2> mpDirectDraw; 656 COMReference<IDirectDrawSurface> mpPrimarySurface; 657 COMReference<IDirectDrawSurface> mpBackBufferSurface; 658 659 COMReference< IDirect3D2 > mpDirect3D; 660 COMReference< IDirect3DDevice2 > mpDirect3DDevice; 661 662 mutable ::canvas::tools::ElapsedTime maLastUpdate; // for the frame counter 663 664 D3DDEVICEDESC maDeviceDesc; 665 666 typedef std::vector<canvas::Vertex> vertexCache_t; 667 vertexCache_t maVertexCache; 668 std::size_t mnCount; 669 670 int mnBeginSceneCount; 671 672 const bool mbPageFlipping; 673 bool mbHasNoTearingBlt; 674 bool mbError; 675 PrimitiveType meType; 676 677 ::canvas::ISurfaceSharedPtr mpTexture; 678 ::basegfx::B2IVector maPageSize; 679 }; 680 681 ::osl::Mutex DXRenderModule::maMutex; 682 683 ////////////////////////////////////////////////////////////////////////////////// 684 // DXSurface::ImplRenderModuleGuard 685 ////////////////////////////////////////////////////////////////////////////////// 686 687 inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard( 688 DXRenderModule& rRenderModule ) : 689 mrRenderModule( rRenderModule ) 690 { 691 mrRenderModule.lock(); 692 } 693 694 inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard() 695 { 696 mrRenderModule.unlock(); 697 } 698 699 #ifdef FAKE_MAX_NUMBER_TEXTURES 700 static sal_uInt32 gNumSurfaces = 0; 701 #endif 702 703 void fillRect( sal_uInt32 *pDest, 704 sal_uInt32 dwWidth, 705 sal_uInt32 dwHeight, 706 sal_uInt32 dwPitch, 707 sal_uInt32 dwColor ) 708 { 709 for(sal_uInt32 i=0; i<dwWidth; ++i) 710 { 711 pDest[i]=dwColor; 712 pDest[((dwHeight-1)*dwPitch)+i]=dwColor; 713 } 714 715 for(sal_uInt32 j=0; j<dwHeight; ++j) 716 { 717 pDest[0]=dwColor; 718 pDest[dwWidth-1]=dwColor; 719 pDest += dwPitch; 720 } 721 } 722 723 ////////////////////////////////////////////////////////////////////////////////// 724 // DXSurface::DXSurface 725 ////////////////////////////////////////////////////////////////////////////////// 726 727 DXSurface::DXSurface( DXRenderModule& rRenderModule, 728 const ::basegfx::B2ISize& rSize ) : 729 mrRenderModule(rRenderModule), 730 mpTexture(NULL), 731 mpSurface(NULL), 732 maSize() 733 { 734 ImplRenderModuleGuard aGuard( mrRenderModule ); 735 736 #ifdef FAKE_MAX_NUMBER_TEXTURES 737 ++gNumSurfaces; 738 if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES) 739 return; 740 #endif 741 742 #ifdef FAKE_MAX_TEXTURE_SIZE 743 if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE) 744 return; 745 if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE) 746 return; 747 #endif 748 749 ENSURE_ARG_OR_THROW(rSize.getX() > 0 && rSize.getY() > 0, 750 "DXSurface::DXSurface(): request for zero-sized surface"); 751 752 const D3DDEVICEDESC &deviceDesc = rRenderModule.getDeviceDesc(); 753 754 DDSURFACEDESC aSurfaceDesc; 755 rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 ); 756 aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); 757 aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; 758 aSurfaceDesc.dwWidth = ::std::min(deviceDesc.dwMaxTextureWidth,::canvas::tools::nextPow2(rSize.getX())); 759 aSurfaceDesc.dwHeight = ::std::min(deviceDesc.dwMaxTextureHeight,::canvas::tools::nextPow2(rSize.getY())); 760 aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE | 761 DDSCAPS_VIDEOMEMORY | 762 DDSCAPS_LOCALVIDMEM; 763 rtl_copyMemory(&aSurfaceDesc.ddpfPixelFormat,&rRenderModule.getTextureFormat(),sizeof(DDPIXELFORMAT)); 764 765 IDirectDrawSurface *pSurface; 766 COMReference<IDirectDraw2> pDirectDraw(rRenderModule.getDirectDraw()); 767 HRESULT hr = pDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); 768 if(FAILED(hr)) 769 { 770 // if the call failed due to 'out of videomemory', 771 // retry with request for AGP memory. 772 if(DDERR_OUTOFVIDEOMEMORY == hr) 773 { 774 aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE | 775 DDSCAPS_VIDEOMEMORY | 776 DDSCAPS_NONLOCALVIDMEM; 777 hr = pDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); 778 } 779 } 780 781 if(SUCCEEDED(hr)) 782 { 783 IDirect3DTexture2* pTexture; 784 if( FAILED(pSurface->QueryInterface(IID_IDirect3DTexture2, (LPVOID *)&pTexture)) ) 785 { 786 pSurface->Release(); 787 return; 788 } 789 790 maSize.setX(aSurfaceDesc.dwWidth); 791 maSize.setY(aSurfaceDesc.dwHeight); 792 793 mpSurface=COMReference<IDirectDrawSurface>(pSurface); 794 mpTexture=COMReference<IDirect3DTexture2>(pTexture); 795 796 // #122683# Clear texture, to avoid ugly artifacts at the 797 // border to invisible sprite areas (note that the textures 798 // are usually only partly utilized). 799 clearSurface( mpSurface ); 800 } 801 } 802 803 ////////////////////////////////////////////////////////////////////////////////// 804 // DXSurface::~DXSurface 805 ////////////////////////////////////////////////////////////////////////////////// 806 807 DXSurface::~DXSurface() 808 { 809 ImplRenderModuleGuard aGuard( mrRenderModule ); 810 811 #ifdef FAKE_MAX_NUMBER_TEXTURES 812 gNumSurfaces--; 813 #endif 814 } 815 816 ////////////////////////////////////////////////////////////////////////////////// 817 // DXSurface::selectTexture 818 ////////////////////////////////////////////////////////////////////////////////// 819 820 bool DXSurface::selectTexture() 821 { 822 ImplRenderModuleGuard aGuard( mrRenderModule ); 823 824 mrRenderModule.flushVertexCache(); 825 826 D3DTEXTUREHANDLE aTextureHandle; 827 if(FAILED(mpTexture->GetHandle( 828 mrRenderModule.getDevice().get(), 829 &aTextureHandle))) 830 { 831 return false; 832 } 833 834 // select texture for next primitive 835 if(FAILED(mrRenderModule.getDevice()->SetRenderState( 836 D3DRENDERSTATE_TEXTUREHANDLE,aTextureHandle))) 837 { 838 return false; 839 } 840 841 #if defined(DX_DEBUG_IMAGES) 842 # if OSL_DEBUG_LEVEL > 0 843 if( mpSurface.is() ) 844 { 845 DDSURFACEDESC aSurfaceDesc; 846 rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 ); 847 aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); 848 849 if( SUCCEEDED(mpSurface->Lock( NULL, 850 &aSurfaceDesc, 851 DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY, 852 NULL)) ) 853 { 854 imdebug( "rgba w=%d h=%d %p", 855 aSurfaceDesc.dwWidth, 856 aSurfaceDesc.dwHeight, 857 aSurfaceDesc.lpSurface ); 858 859 mpSurface->Unlock(NULL); 860 } 861 } 862 # endif 863 #endif 864 865 return true; 866 } 867 868 ////////////////////////////////////////////////////////////////////////////////// 869 // DXSurface::isValid 870 ////////////////////////////////////////////////////////////////////////////////// 871 872 bool DXSurface::isValid() 873 { 874 ImplRenderModuleGuard aGuard( mrRenderModule ); 875 876 if(!(mpSurface.is())) 877 return false; 878 879 if(mpSurface->IsLost() == DDERR_SURFACELOST) 880 { 881 mpSurface->Restore(); 882 return false; 883 } 884 885 return true; 886 } 887 888 ////////////////////////////////////////////////////////////////////////////////// 889 // DXSurface::update 890 ////////////////////////////////////////////////////////////////////////////////// 891 892 bool DXSurface::update( const ::basegfx::B2IPoint& rDestPos, 893 const ::basegfx::B2IRange& rSourceRect, 894 ::canvas::IColorBuffer& rSource ) 895 { 896 ImplRenderModuleGuard aGuard( mrRenderModule ); 897 898 // can't update if surface is not valid, that means 899 // either not existent nor restored... 900 if(!(isValid())) 901 return false; 902 903 DDSURFACEDESC aSurfaceDesc; 904 rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 ); 905 aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); 906 907 // TODO(P2): only lock the region we want to update 908 if( FAILED(mpSurface->Lock( NULL, 909 &aSurfaceDesc, 910 DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_WRITEONLY, 911 NULL)) ) 912 return false; 913 914 if(sal_uInt8* pImage = rSource.lock()) 915 { 916 switch( rSource.getFormat() ) 917 { 918 case ::canvas::IColorBuffer::FMT_A8R8G8B8: 919 { 920 const std::size_t nSourceBytesPerPixel(4); 921 const std::size_t nSourcePitchInBytes(rSource.getStride()); 922 pImage += rSourceRect.getMinY()*nSourcePitchInBytes; 923 pImage += rSourceRect.getMinX()*nSourceBytesPerPixel; 924 925 // calculate the destination memory address 926 sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+ 927 (rDestPos.getY()*aSurfaceDesc.lPitch) + 928 (4*rDestPos.getX())); 929 930 const sal_uInt32 nNumBytesToCopy( 931 static_cast<sal_uInt32>( 932 rSourceRect.getWidth())* 933 nSourceBytesPerPixel); 934 const sal_uInt64 nNumLines(rSourceRect.getHeight()); 935 936 for(sal_uInt32 i=0; i<nNumLines; ++i) 937 { 938 rtl_copyMemory(pDst,pImage,nNumBytesToCopy); 939 940 pDst += aSurfaceDesc.lPitch; 941 pImage += nSourcePitchInBytes; 942 } 943 } 944 break; 945 946 case ::canvas::IColorBuffer::FMT_R8G8B8: 947 { 948 const std::size_t nSourceBytesPerPixel(3); 949 const std::size_t nSourcePitchInBytes(rSource.getStride()); 950 pImage += rSourceRect.getMinY()*nSourcePitchInBytes; 951 pImage += rSourceRect.getMinX()*nSourceBytesPerPixel; 952 953 // calculate the destination memory address 954 sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+ 955 (rDestPos.getY()*aSurfaceDesc.lPitch) + 956 (4*rDestPos.getX())); 957 958 const sal_uInt64 nNumColumns(rSourceRect.getWidth()); 959 const sal_uInt64 nNumLines(rSourceRect.getHeight()); 960 for(sal_uInt32 i=0; i<nNumLines; ++i) 961 { 962 sal_uInt32 *pDstScanline = reinterpret_cast<sal_uInt32 *>(pDst); 963 sal_uInt8 *pSrcScanline = reinterpret_cast<sal_uInt8 *>(pImage); 964 for(sal_uInt32 x=0; x<nNumColumns; ++x) 965 { 966 sal_uInt32 color(0xFF000000); 967 color |= pSrcScanline[2]<<16; 968 color |= pSrcScanline[1]<<8; 969 color |= pSrcScanline[0]; 970 pSrcScanline += 3; 971 *pDstScanline++ = color; 972 } 973 974 pDst += aSurfaceDesc.lPitch; 975 pImage += nSourcePitchInBytes; 976 } 977 } 978 break; 979 980 case ::canvas::IColorBuffer::FMT_X8R8G8B8: 981 { 982 const std::size_t nSourceBytesPerPixel(4); 983 const std::size_t nSourcePitchInBytes(rSource.getStride()); 984 pImage += rSourceRect.getMinY()*nSourcePitchInBytes; 985 pImage += rSourceRect.getMinX()*nSourceBytesPerPixel; 986 987 // calculate the destination memory address 988 sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+ 989 (rDestPos.getY()*aSurfaceDesc.lPitch) + 990 (4*rDestPos.getX())); 991 992 const sal_uInt64 nNumLines(rSourceRect.getHeight()); 993 994 for(sal_uInt32 i=0; i<nNumLines; ++i) 995 { 996 sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage); 997 sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst); 998 for(sal_uInt32 j=0; j<rSourceRect.getWidth(); ++j) 999 pDst32[j] = 0xFF000000 | pSrc32[j]; 1000 1001 pDst += aSurfaceDesc.lPitch; 1002 pImage += nSourcePitchInBytes; 1003 } 1004 } 1005 break; 1006 1007 default: 1008 ENSURE_OR_RETURN_FALSE(false, 1009 "DXSurface::update(): Unknown/unimplemented buffer format" ); 1010 break; 1011 } 1012 1013 rSource.unlock(); 1014 } 1015 1016 return SUCCEEDED(mpSurface->Unlock(NULL)); 1017 } 1018 1019 ////////////////////////////////////////////////////////////////////////////////// 1020 // DXSurface::getSize 1021 ////////////////////////////////////////////////////////////////////////////////// 1022 1023 ::basegfx::B2IVector DXSurface::getSize() 1024 { 1025 return maSize; 1026 } 1027 1028 ////////////////////////////////////////////////////////////////////////////////// 1029 // DXRenderModule::DXRenderModule 1030 ////////////////////////////////////////////////////////////////////////////////// 1031 1032 DXRenderModule::DXRenderModule( const ::Window& rWindow ) : 1033 mhWnd(0), 1034 mpWindow(), 1035 maSize(), 1036 maSelectedFullscreenMode(), 1037 maTextureFormat(), 1038 maMonitorInfo(), 1039 mpDirectDraw(), 1040 mpPrimarySurface(), 1041 mpBackBufferSurface(), 1042 mpDirect3D(), 1043 mpDirect3DDevice(), 1044 maLastUpdate(), 1045 maDeviceDesc(), 1046 maVertexCache(), 1047 mnCount(0), 1048 mnBeginSceneCount(0), 1049 mbPageFlipping( false ), 1050 mbHasNoTearingBlt( false ), 1051 mbError( false ), 1052 meType( PRIMITIVE_TYPE_UNKNOWN ), 1053 mpTexture(), 1054 maPageSize() 1055 { 1056 // TODO(P2): get rid of those fine-grained locking 1057 ::osl::MutexGuard aGuard( maMutex ); 1058 1059 if(!(create(rWindow))) 1060 { 1061 throw lang::NoSupportException( 1062 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( 1063 "Could not create DirectX device!") ),NULL); 1064 } 1065 1066 // allocate a single texture surface which can be used later. 1067 // we also use this to calibrate the page size. 1068 ::basegfx::B2IVector aPageSize( 1069 ::std::min( 1070 static_cast<sal_uInt32>(maDeviceDesc.dwMaxTextureWidth), 1071 static_cast<sal_uInt32>(MAX_TEXTURE_SIZE)), 1072 ::std::min( 1073 static_cast<sal_uInt32>(maDeviceDesc.dwMaxTextureHeight), 1074 static_cast<sal_uInt32>(MAX_TEXTURE_SIZE))); 1075 while(true) 1076 { 1077 mpTexture = ::canvas::ISurfaceSharedPtr( 1078 new DXSurface(*this,aPageSize)); 1079 if(mpTexture->isValid()) 1080 break; 1081 1082 aPageSize.setX(aPageSize.getX()>>1); 1083 aPageSize.setY(aPageSize.getY()>>1); 1084 if((aPageSize.getX() < MIN_TEXTURE_SIZE) || 1085 (aPageSize.getY() < MIN_TEXTURE_SIZE)) 1086 { 1087 throw lang::NoSupportException( 1088 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( 1089 "Could not create DirectX device!") ),NULL); 1090 } 1091 } 1092 maPageSize=aPageSize; 1093 } 1094 1095 ////////////////////////////////////////////////////////////////////////////////// 1096 // DXRenderModule::create 1097 ////////////////////////////////////////////////////////////////////////////////// 1098 1099 bool DXRenderModule::create( const ::Window& rWindow ) 1100 { 1101 // TODO(P2): get rid of those fine-grained locking 1102 ::osl::MutexGuard aGuard( maMutex ); 1103 1104 maVertexCache.reserve(1024); 1105 1106 mpWindow.reset( 1107 new SystemChildWindow( 1108 const_cast<Window *>(&rWindow), 0) ); 1109 1110 // system child window must not receive mouse events 1111 mpWindow->SetMouseTransparent( sal_True ); 1112 1113 // parent should receive paint messages as well 1114 // [PARENTCLIPMODE_NOCLIP], the argument is here 1115 // passed as plain numeric value since the stupid 1116 // define utilizes a USHORT cast. 1117 mpWindow->SetParentClipMode(0x0002); 1118 1119 // the system child window must not clear its background 1120 mpWindow->EnableEraseBackground( sal_False ); 1121 1122 mpWindow->SetControlForeground(); 1123 mpWindow->SetControlBackground(); 1124 mpWindow->EnablePaint(sal_False); 1125 1126 const SystemEnvData *pData = mpWindow->GetSystemData(); 1127 const HWND hwnd(reinterpret_cast<HWND>(pData->hWnd)); 1128 mhWnd = const_cast<HWND>(hwnd); 1129 1130 ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND>(mhWnd) ), 1131 "DXRenderModuleDXRenderModuleWin32() No valid HWND given." ); 1132 1133 // retrieve position and size of the parent window 1134 const ::Size &rSizePixel(rWindow.GetSizePixel()); 1135 1136 // remember the size of the parent window, since we 1137 // need to use this for our child window. 1138 maSize.setX(static_cast<sal_Int32>(rSizePixel.Width())); 1139 maSize.setY(static_cast<sal_Int32>(rSizePixel.Height())); 1140 1141 // let the child window cover the same size as the parent window. 1142 mpWindow->SetPosSizePixel(0,0,maSize.getX(),maSize.getY()); 1143 1144 MonitorList aMonitorList; 1145 fillMonitorList( aMonitorList ); 1146 1147 mpDirectDraw = COMReference<IDirectDraw2>( 1148 createDirectDraw(aMonitorList, maMonitorInfo, mhWnd)); 1149 1150 if(!mpDirectDraw.is()) 1151 return false; 1152 1153 if( !queryCaps() ) 1154 { 1155 // go defunct, and exit 1156 VERBOSE_TRACE( "Device::Device(): GetCaps failed" ); 1157 mpDirectDraw.reset(); 1158 return false; 1159 } 1160 1161 if( !validateCaps() ) 1162 { 1163 // go defunct, and exit 1164 VERBOSE_TRACE( "Device::Device(): Insufficient DirectX capabilities, failed" ); 1165 mpDirectDraw.reset(); 1166 return false; 1167 } 1168 1169 if( FAILED( mpDirectDraw->SetCooperativeLevel( mhWnd, 1170 DDSCL_NORMAL|DDSCL_MULTITHREADED|DDSCL_FPUPRESERVE ) ) ) 1171 { 1172 // go defunct, and exit 1173 VERBOSE_TRACE( "Device::Device(): SetCooperativeLevel failed" ); 1174 mpDirectDraw.reset(); 1175 return false; 1176 } 1177 1178 // setup query struct 1179 rtl_fillMemory( &maSelectedFullscreenMode.selectedDesc, 1180 sizeof(DDSURFACEDESC), 0 ); 1181 maSelectedFullscreenMode.selectedDesc.dwSize = sizeof(DDSURFACEDESC); 1182 1183 // read current display mode, e.g. for screen dimension 1184 if( FAILED( mpDirectDraw->GetDisplayMode( &maSelectedFullscreenMode.selectedDesc )) ) 1185 { 1186 // go defunct, and exit 1187 VERBOSE_TRACE( "Device::Device(): GetDisplayMode failed" ); 1188 mpDirectDraw.reset(); 1189 return false; 1190 } 1191 1192 // check for supported primary surface formats... 1193 unsigned int nDisplayFormat = getDisplayFormat() & 0x00000FFF; 1194 if(nDisplayFormat != 0x888 && nDisplayFormat != 0x565) 1195 { 1196 // go defunct, and exit 1197 VERBOSE_TRACE( "Device::Device(): Unsupported DisplayFormat" ); 1198 mpDirectDraw.reset(); 1199 return false; 1200 } 1201 1202 // create primary surface reference 1203 DDSURFACEDESC aSurfaceDesc; 1204 IDirectDrawSurface* pPrimarySurface; 1205 1206 rtl_fillMemory( &aSurfaceDesc, 1207 sizeof(DDSURFACEDESC), 0 ); 1208 aSurfaceDesc.dwSize = sizeof(aSurfaceDesc); 1209 aSurfaceDesc.dwFlags = DDSD_CAPS; 1210 aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE; 1211 1212 if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pPrimarySurface, NULL)) ) 1213 { 1214 // go defunct, and exit 1215 VERBOSE_TRACE( "Device::Device(): CreateSurface failed" ); 1216 mpDirectDraw.reset(); 1217 return false; 1218 } 1219 1220 mpPrimarySurface = COMReference< IDirectDrawSurface >(pPrimarySurface); 1221 1222 // create a Clipper and associate it with the primary surface 1223 // and the render window 1224 LPDIRECTDRAWCLIPPER pClipper; 1225 if( FAILED(mpDirectDraw->CreateClipper( 0, &pClipper, NULL )) ) 1226 { 1227 // go defunct, and exit 1228 VERBOSE_TRACE( "Device::Device(): CreateClipper failed" ); 1229 mpPrimarySurface.reset(); 1230 mpDirectDraw.reset(); 1231 return false; 1232 } 1233 if( FAILED(pClipper->SetHWnd(0, mhWnd)) ) 1234 { 1235 // go defunct, and exit 1236 VERBOSE_TRACE( "Device::Device(): Clipper->SetHWnd failed" ); 1237 pClipper->Release(); 1238 mpPrimarySurface.reset(); 1239 mpDirectDraw.reset(); 1240 return false; 1241 } 1242 if( FAILED(mpPrimarySurface->SetClipper( pClipper )) ) 1243 { 1244 // go defunct, and exit 1245 VERBOSE_TRACE( "Device::Device(): SetClipper failed" ); 1246 pClipper->Release(); 1247 mpPrimarySurface.reset(); 1248 mpDirectDraw.reset(); 1249 return false; 1250 } 1251 1252 // clipper is now owned by mpPrimarySurface, release our reference 1253 pClipper->Release(); 1254 1255 // TODO(F3): Check whether palette needs any setup here 1256 1257 // get us a backbuffer for simulated flipping 1258 IDirectDrawSurface* pSurface; 1259 1260 // Strictly speaking, we don't need a full screen worth of 1261 // backbuffer here. We could also scale dynamically with 1262 // the current window size, but this will make it 1263 // necessary to temporarily have two buffers while copying 1264 // from the old to the new one. What's more, at the time 1265 // we need a larger buffer, DX might not have sufficient 1266 // resources available, and we're then left with too small 1267 // a back buffer, and no way of falling back to a 1268 // different canvas implementation. 1269 const ::basegfx::B2ISize aSize( getFramebufferSize() ); 1270 1271 rtl_fillMemory( &aSurfaceDesc, 1272 sizeof(DDSURFACEDESC), 0 ); 1273 aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); 1274 aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; 1275 aSurfaceDesc.dwHeight= aSize.getY(); 1276 aSurfaceDesc.dwWidth = aSize.getX(); 1277 1278 aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; 1279 1280 HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); 1281 1282 if( FAILED( nRes ) ) 1283 { 1284 if( nRes == DDERR_OUTOFVIDEOMEMORY ) 1285 { 1286 // local vid mem failed. Maybe AGP mem works? 1287 aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; 1288 if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL)) ) 1289 { 1290 // no chance, go defunct, and exit 1291 VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" ); 1292 mpPrimarySurface.reset(); 1293 mpDirectDraw.reset(); 1294 return false; 1295 } 1296 1297 VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer reverted to non-local video mem" ); 1298 } 1299 else 1300 { 1301 // no chance, go defunct, and exit 1302 VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" ); 1303 mpPrimarySurface.reset(); 1304 mpDirectDraw.reset(); 1305 return false; 1306 } 1307 } 1308 1309 VERBOSE_TRACE( "Device::Device(): created backbuffer of size %d times %d pixel", 1310 aSurfaceDesc.dwWidth, 1311 aSurfaceDesc.dwHeight ); 1312 1313 mpBackBufferSurface = COMReference< IDirectDrawSurface >(pSurface); 1314 clearSurface(mpBackBufferSurface); 1315 1316 if( !setup3DDevice() ) 1317 { 1318 // go defunct, and exit 1319 VERBOSE_TRACE( "Device::Device(): setup3DDevice failed" ); 1320 mpBackBufferSurface.reset(); 1321 mpPrimarySurface.reset(); 1322 mpDirectDraw.reset(); 1323 return false; 1324 } 1325 1326 mpWindow->Show(); 1327 1328 return true; 1329 } 1330 1331 ////////////////////////////////////////////////////////////////////////////////// 1332 // DXRenderModule::getSize 1333 ////////////////////////////////////////////////////////////////////////////////// 1334 1335 ::basegfx::B2ISize DXRenderModule::getFramebufferSize() const 1336 { 1337 return mpDirectDraw.is() ? 1338 ::basegfx::B2ISize( maSelectedFullscreenMode.selectedDesc.dwWidth, 1339 maSelectedFullscreenMode.selectedDesc.dwHeight ) : 1340 ::basegfx::B2ISize(); 1341 } 1342 1343 ////////////////////////////////////////////////////////////////////////////////// 1344 // DXRenderModule::setup3DDevice 1345 ////////////////////////////////////////////////////////////////////////////////// 1346 1347 bool DXRenderModule::setup3DDevice() 1348 { 1349 // create and setup 3D device 1350 // ========================== 1351 LPDIRECT3D2 pDirect3D; 1352 if( FAILED( mpDirectDraw->QueryInterface( IID_IDirect3D2, (LPVOID*)&pDirect3D ) ) ) 1353 { 1354 // go defunct, and exit 1355 VERBOSE_TRACE( "Device::setup3DDevice(): QueryInterface() for Direct3D failed" ); 1356 return false; 1357 } 1358 1359 mpDirect3D = COMReference< IDirect3D2 >(pDirect3D); 1360 1361 LPDIRECT3DDEVICE2 pDirect3DDevice; 1362 // try HW-accelerated device first 1363 if( FAILED(mpDirect3D->CreateDevice( IID_IDirect3DHALDevice, 1364 mpBackBufferSurface.get(), 1365 &pDirect3DDevice )) ) 1366 { 1367 // no HW 3D support - go defunct, and exit 1368 VERBOSE_TRACE( "Device::setup3DDevice(): CreateDevice() for HW Direct3D rendering failed" ); 1369 mpDirect3D.reset(); 1370 return false; 1371 } 1372 1373 D3DDEVICEDESC aHELDeviceDesc; 1374 rtl_fillMemory(&maDeviceDesc,sizeof(maDeviceDesc),0); 1375 rtl_fillMemory(&aHELDeviceDesc,sizeof(aHELDeviceDesc),0); 1376 maDeviceDesc.dwSize = sizeof(maDeviceDesc); 1377 aHELDeviceDesc.dwSize = sizeof(aHELDeviceDesc); 1378 if(FAILED(pDirect3DDevice->GetCaps(&maDeviceDesc,&aHELDeviceDesc))) 1379 { 1380 // go defunct, and exit 1381 VERBOSE_TRACE( "Device::setup3DDevice(): GetCaps() for Direct3DDevice failed" ); 1382 mpDirect3D.reset(); 1383 return false; 1384 } 1385 1386 mpDirect3DDevice = COMReference< IDirect3DDevice2 >(pDirect3DDevice); 1387 1388 // select appropriate texture format (_need_ alpha channel here) 1389 rtl_fillMemory( &maTextureFormat, 1390 sizeof(DDPIXELFORMAT), 0 ); 1391 maTextureFormat.dwSize = sizeof(DDPIXELFORMAT); 1392 if( SUCCEEDED(mpDirect3DDevice->EnumTextureFormats( EnumTextureFormatsCallback, &maTextureFormat )) ) 1393 { 1394 bool bSupportedFormat = true; 1395 if((maTextureFormat.dwFlags & (DDPF_ALPHAPIXELS | DDPF_RGB)) != (DDPF_ALPHAPIXELS | DDPF_RGB)) 1396 bSupportedFormat = false; 1397 else if(maTextureFormat.dwRGBAlphaBitMask != 0xFF000000) 1398 bSupportedFormat = false; 1399 else if(maTextureFormat.dwRBitMask != 0x00FF0000) 1400 bSupportedFormat = false; 1401 else if(maTextureFormat.dwGBitMask != 0x0000FF00) 1402 bSupportedFormat = false; 1403 else if(maTextureFormat.dwBBitMask != 0x000000FF) 1404 bSupportedFormat = false; 1405 1406 if(bSupportedFormat) 1407 { 1408 VERBOSE_TRACE( "Device::setup3DDevice(): chose texture format dwRGBBitCount %d, dwRBitMask %x, " 1409 "dwGBitMask %x, dwBBitMask %x and dwRGBAlphaBitMask %x. The texture uses %s alpha.", 1410 maTextureFormat.dwRGBBitCount, 1411 maTextureFormat.dwRBitMask, 1412 maTextureFormat.dwGBitMask, 1413 maTextureFormat.dwBBitMask, 1414 maTextureFormat.dwRGBAlphaBitMask, 1415 maTextureFormat.dwFlags & DDPF_ALPHAPREMULT ? "premultiplied" : "non-premultiplied" ); 1416 1417 // setup the device (with as much as we can possibly do here) 1418 // ========================================================== 1419 1420 LPDIRECT3DVIEWPORT2 pViewport; 1421 1422 if( SUCCEEDED(mpDirect3D->CreateViewport( &pViewport, NULL )) ) 1423 { 1424 if( SUCCEEDED(mpDirect3DDevice->AddViewport( pViewport )) ) 1425 { 1426 // setup viewport (to whole backbuffer) 1427 D3DVIEWPORT2 aViewport; 1428 1429 aViewport.dwSize = sizeof(D3DVIEWPORT2); 1430 aViewport.dwX = 0; 1431 aViewport.dwY = 0; 1432 aViewport.dwWidth = maSelectedFullscreenMode.selectedDesc.dwWidth; 1433 aViewport.dwHeight = maSelectedFullscreenMode.selectedDesc.dwHeight; 1434 aViewport.dvClipX = -1.0; 1435 aViewport.dvClipY = -1.0; 1436 aViewport.dvClipWidth = 2.0; 1437 aViewport.dvClipHeight = 2.0; 1438 aViewport.dvMinZ = 0.0; 1439 aViewport.dvMaxZ = 1.0; 1440 1441 if( SUCCEEDED(pViewport->SetViewport2( &aViewport )) ) 1442 { 1443 if( SUCCEEDED(mpDirect3DDevice->SetCurrentViewport( pViewport )) ) 1444 { 1445 // Viewport was handed over to 3DDevice, thus we can release now 1446 pViewport->Release(); 1447 1448 // currently, no need for any 1449 // matrix or light source 1450 // setup, since we only render 1451 // transformed&lighted 1452 // vertices 1453 1454 // done; successfully 1455 return true; 1456 } 1457 else 1458 { 1459 VERBOSE_TRACE( "Device::setup3DDevice(): SetCurrentViewport failed" ); 1460 } 1461 } 1462 else 1463 { 1464 VERBOSE_TRACE( "Device::setup3DDevice(): SetViewport2 failed" ); 1465 } 1466 } 1467 else 1468 { 1469 VERBOSE_TRACE( "Device::setup3DDevice(): AddViewport failed" ); 1470 } 1471 1472 pViewport->Release(); 1473 } 1474 else 1475 { 1476 VERBOSE_TRACE( "Device::setup3DDevice(): CreateViewport failed" ); 1477 } 1478 } 1479 else 1480 { 1481 VERBOSE_TRACE( "Device::setup3DDevice(): No supported pixelformat" ); 1482 } 1483 } 1484 else 1485 { 1486 VERBOSE_TRACE( "Device::setup3DDevice(): EnumTextureFormats failed" ); 1487 } 1488 1489 // go defunct, and exit 1490 mpDirect3DDevice.reset(); 1491 mpDirect3D.reset(); 1492 1493 return false; 1494 } 1495 1496 ////////////////////////////////////////////////////////////////////////////////// 1497 // DXRenderModule::queryCaps 1498 ////////////////////////////////////////////////////////////////////////////////// 1499 1500 bool DXRenderModule::queryCaps() 1501 { 1502 DDCAPS aHWCaps; 1503 DDCAPS aHELCaps; 1504 1505 rtl_fillMemory( &aHWCaps, 1506 sizeof(aHWCaps), 0 ); 1507 rtl_fillMemory( &aHELCaps, 1508 sizeof(aHELCaps), 0 ); 1509 aHWCaps.dwSize = sizeof( aHWCaps ); 1510 aHELCaps.dwSize = sizeof( aHELCaps ); 1511 1512 if( FAILED( mpDirectDraw->GetCaps( &aHWCaps, 1513 &aHELCaps ) ) ) 1514 { 1515 return false; 1516 } 1517 1518 mbHasNoTearingBlt = aHWCaps.dwFXCaps & DDBLTFX_NOTEARING; 1519 1520 VERBOSE_TRACE( "dxcanvas initialization: %d bytes VRAM free for surfaces (%d with AGP mem), " 1521 "%d bytes VRAM free for textures (%d with AGP mem)", 1522 getAvailableSurfaceMem( false ), 1523 getAvailableSurfaceMem( true ), 1524 getAvailableTextureMem( false ), 1525 getAvailableTextureMem( true ) ); 1526 1527 return true; 1528 } 1529 1530 ////////////////////////////////////////////////////////////////////////////////// 1531 // DXRenderModule::validateCaps 1532 ////////////////////////////////////////////////////////////////////////////////// 1533 1534 bool DXRenderModule::validateCaps() 1535 { 1536 // TODO(E3): Validate HW capabilities. Depending on primary 1537 // surface size, reject HW e.g. on the grounds of insufficient 1538 // VRAM. 1539 1540 // setup query struct 1541 DDSURFACEDESC desc; 1542 rtl_fillMemory(&desc,sizeof(DDSURFACEDESC),0); 1543 desc.dwSize = sizeof(DDSURFACEDESC); 1544 1545 // read current display mode, e.g. for screen dimension 1546 if(FAILED( mpDirectDraw->GetDisplayMode(&desc))) 1547 return false; 1548 1549 // simple heuristic: we need at least 3 times the desktop 1550 // resolution based on ARGB color values... 1551 std::size_t nMinimumVRAMSize = ((desc.dwWidth*desc.dwHeight)<<2)*3; 1552 if(getAvailableSurfaceMem() < nMinimumVRAMSize) 1553 return false; 1554 1555 return true; 1556 } 1557 1558 ////////////////////////////////////////////////////////////////////////////////// 1559 // DXRenderModule::getDisplayFormat 1560 ////////////////////////////////////////////////////////////////////////////////// 1561 1562 unsigned int DXRenderModule::getDisplayFormat() const 1563 { 1564 unsigned int nFormat; 1565 nFormat = ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwRGBAlphaBitMask)<<12; 1566 nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwRBitMask)<<8; 1567 nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwGBitMask)<<4; 1568 nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwBBitMask); 1569 return nFormat; 1570 } 1571 1572 ////////////////////////////////////////////////////////////////////////////////// 1573 // DXRenderModule::getAvailableSurfaceMem 1574 ////////////////////////////////////////////////////////////////////////////////// 1575 1576 std::size_t DXRenderModule::getAvailableSurfaceMem( bool bWithAGPMem ) const 1577 { 1578 if( !mpDirectDraw.is() ) 1579 return 0; 1580 1581 std::size_t nRes( 0 ); 1582 1583 DDSCAPS aSurfaceCaps; 1584 DWORD nTotal, nFree; 1585 1586 // real VRAM (const_cast, since GetAvailableVidMem is non-const) 1587 aSurfaceCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; 1588 if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) ) 1589 return 0; 1590 1591 nRes += nFree; 1592 1593 if( bWithAGPMem ) 1594 { 1595 // AGP RAM (const_cast, since GetAvailableVidMem is non-const) 1596 aSurfaceCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; 1597 if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) ) 1598 return 0; 1599 1600 nRes += nFree; 1601 } 1602 1603 return nRes; 1604 } 1605 1606 ////////////////////////////////////////////////////////////////////////////////// 1607 // DXRenderModule::getAvailableTextureMem 1608 ////////////////////////////////////////////////////////////////////////////////// 1609 1610 std::size_t DXRenderModule::getAvailableTextureMem( bool bWithAGPMem ) const 1611 { 1612 if( !mpDirectDraw.is() ) 1613 return 0; 1614 1615 std::size_t nRes( 0 ); 1616 1617 DDSCAPS aSurfaceCaps; 1618 DWORD nTotal, nFree; 1619 1620 // TODO(F1): Check if flags are applicable 1621 1622 // real VRAM (const_cast, since GetAvailableVidMem is non-const) 1623 aSurfaceCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; 1624 if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) ) 1625 return 0; 1626 1627 nRes += nFree; 1628 1629 if( bWithAGPMem ) 1630 { 1631 // AGP RAM (const_cast, since GetAvailableVidMem is non-const) 1632 aSurfaceCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; 1633 if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) ) 1634 return 0; 1635 1636 nRes += nFree; 1637 } 1638 1639 // TODO(F1): Add pool mem 1640 1641 return nRes; 1642 } 1643 1644 ////////////////////////////////////////////////////////////////////////////////// 1645 // DXRenderModule::convert2Screen 1646 ////////////////////////////////////////////////////////////////////////////////// 1647 1648 void DXRenderModule::convert2Screen( ::basegfx::B2IPoint& io_rDestPos, 1649 ::basegfx::B2IRange& io_rDestArea ) 1650 { 1651 POINT aPoint = { 0, 0 }; 1652 ClientToScreen( mhWnd, &aPoint ); 1653 1654 // i52230 make sure given screen coordinate is relative to 1655 // this monitor's area (the device rendering is always 1656 // contained to a single monitor) 1657 aPoint.x -= maMonitorInfo.rcMonitor.left; 1658 aPoint.y -= maMonitorInfo.rcMonitor.top; 1659 1660 io_rDestPos.setX( io_rDestPos.getX() + aPoint.x ); 1661 io_rDestPos.setY( io_rDestPos.getY() + aPoint.y ); 1662 1663 const ::basegfx::B2ISize& rSize( getFramebufferSize() ); 1664 1665 // calc output bounds (clip against framebuffer bounds) 1666 io_rDestArea = ::basegfx::B2IRange( 1667 ::std::max( sal_Int32(0), 1668 ::std::min( sal_Int32(rSize.getX()), 1669 sal_Int32(io_rDestArea.getMinX() + aPoint.x) ) ), 1670 ::std::max( sal_Int32(0), 1671 ::std::min( sal_Int32(rSize.getY()), 1672 sal_Int32(io_rDestArea.getMinY() + aPoint.y) ) ), 1673 ::std::max( sal_Int32(0), 1674 ::std::min( sal_Int32(rSize.getX()), 1675 sal_Int32(io_rDestArea.getMaxX() + aPoint.x) ) ), 1676 ::std::max( sal_Int32(0), 1677 ::std::min( sal_Int32(rSize.getY()), 1678 sal_Int32(io_rDestArea.getMaxY() + aPoint.y) ) ) ); 1679 } 1680 1681 ////////////////////////////////////////////////////////////////////////////////// 1682 // DXRenderModule::createSystemMemorySurface 1683 ////////////////////////////////////////////////////////////////////////////////// 1684 1685 COMReference<IDirectDrawSurface> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize ) 1686 { 1687 DDSURFACEDESC aSurfaceDesc; 1688 IDirectDrawSurface* pSurface; 1689 1690 aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); 1691 aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;; 1692 aSurfaceDesc.dwWidth = rSize.getX(); 1693 aSurfaceDesc.dwHeight= rSize.getY(); 1694 1695 rtl_copyMemory( &aSurfaceDesc.ddpfPixelFormat, &maTextureFormat, sizeof(DDPIXELFORMAT) ); 1696 1697 aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; 1698 1699 HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); 1700 if(FAILED(nRes)) 1701 return COMReference<IDirectDrawSurface>(NULL); 1702 1703 return COMReference<IDirectDrawSurface>(pSurface); 1704 } 1705 1706 ////////////////////////////////////////////////////////////////////////////////// 1707 // DXRenderModule::flip 1708 ////////////////////////////////////////////////////////////////////////////////// 1709 1710 bool DXRenderModule::flip( const ::basegfx::B2IRectangle& rUpdateArea, 1711 const ::basegfx::B2IRectangle& rCurrWindowArea ) 1712 { 1713 // TODO(P2): get rid of those fine-grained locking 1714 ::osl::MutexGuard aGuard( maMutex ); 1715 1716 // see if the main surfaces got lost. if so, try to 1717 // restore them. bail out if this operation fails. 1718 if(!(validateMainSurfaces())) 1719 return false; 1720 1721 flushVertexCache(); 1722 1723 ENSURE_OR_THROW( !mnBeginSceneCount, 1724 "Device::flip(): within 3D scene" ); 1725 1726 // TODO(E3): handle DX errors more thoroughly. For fullscreen 1727 // exclusive mode, actually even our primary surface can get 1728 // lost and needs restore! 1729 1730 if( mpDirectDraw.is() && 1731 mpPrimarySurface.is() && 1732 mpBackBufferSurface.is() ) 1733 { 1734 // ignore area and offset for page flipping device 1735 if( mbPageFlipping ) 1736 { 1737 #if defined(VERBOSE) && defined(DBG_UTIL) 1738 renderFPSCounter(); 1739 renderMemAvailable(); 1740 #endif 1741 VERBOSE_TRACE( "Device::flip(): Using true page flipping" ); 1742 1743 // use true page flipping. Hopefully, the 3D hardware 1744 // is flushed on this flip call (rumours have it that 1745 // way), otherwise, perform the Lock hack as for the 1746 // Blt below. 1747 if( SUCCEEDED(mpPrimarySurface->Flip( NULL, DDFLIP_WAIT )) ) 1748 return true; 1749 } 1750 else 1751 { 1752 VERBOSE_TRACE( "Device::flip(): Using blt for page flipping" ); 1753 1754 // determine actual window position 1755 ::basegfx::B2IPoint aDestPoint( rUpdateArea.getMinimum() ); 1756 ::basegfx::B2IRange aSourceArea( rUpdateArea ); 1757 ::basegfx::B2IRange aDestArea( 0,0, 1758 static_cast<sal_Int32>(rCurrWindowArea.getWidth()), 1759 static_cast<sal_Int32>(rCurrWindowArea.getHeight()) ); 1760 convert2Screen( aDestPoint, aDestArea ); 1761 1762 // perform clipping 1763 if( !::canvas::tools::clipBlit( aSourceArea, 1764 aDestPoint, 1765 rUpdateArea, 1766 aDestArea ) ) 1767 return true; // fully clipped, but still, in a way, 1768 // successful. 1769 1770 // TODO(P1): Rumours have it that the 3D hardware 1771 // _might_ still be rendering with flaky drivers, 1772 // which don't flush properly on Blt(). It was said, 1773 // that 'usually', it works to lock the 3D render 1774 // target (the backbuffer in this case). OTOH, I've 1775 // found that this tends to degrade performance 1776 // significantly on complying cards... 1777 1778 // TODO(P1): Up until rev. 1.3, this method contained 1779 // code to make sure the blit will start _immediately_ 1780 // after the Blt call. If this is not warranted, wait 1781 // for the next vsync. As this case was found to be 1782 // extremely seldom, kicked out (what's more, there's 1783 // simply no guarantee that the blitter will be 1784 // available at any point in the code - Windows still 1785 // is a preemptive multi-processing environment. And 1786 // _if_ we're competing with someone over the blitter, 1787 // we will do so the next VBLANK interval, and the 1788 // following...) 1789 1790 // screen update seems to be smoother when waiting 1791 // for vblank in every case - even when blitter 1792 // supports the DDBLTFX_NOTEARING flag. 1793 if( FAILED(mpDirectDraw->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 1794 NULL)) ) 1795 return false; 1796 1797 DDBLTFX aBltFx; 1798 DDBLTFX* pBltFX = NULL; 1799 if( mbHasNoTearingBlt ) 1800 { 1801 // Blt can internally schedule for no-tearing 1802 // =========================================== 1803 1804 rtl_fillMemory( &aBltFx, 1805 sizeof(aBltFx), 0 ); 1806 aBltFx.dwSize = sizeof(aBltFx); 1807 aBltFx.dwDDFX = DDBLTFX_NOTEARING; 1808 1809 pBltFX = &aBltFx; 1810 } 1811 1812 if( doBlit( aDestPoint, 1813 *mpPrimarySurface, 1814 aSourceArea, 1815 *mpBackBufferSurface, 1816 pBltFX,false ) ) 1817 { 1818 #if defined(VERBOSE) && defined(DBG_UTIL) 1819 renderFPSCounter(); 1820 renderMemAvailable(); 1821 #endif 1822 return true; 1823 } 1824 } 1825 } 1826 return false; 1827 } 1828 1829 ////////////////////////////////////////////////////////////////////////////////// 1830 // DXRenderModule::disposing 1831 ////////////////////////////////////////////////////////////////////////////////// 1832 1833 void DXRenderModule::disposing() 1834 { 1835 if(!(mhWnd)) 1836 return; 1837 1838 mpTexture.reset(); 1839 mpWindow.reset(); 1840 mhWnd=NULL; 1841 1842 // refrain from releasing the DX5 objects - deleting the 1843 // DX5 device seems to kill the whole engine, including 1844 // all objects we might still hold references to 1845 // (surfaces, e.g.) 1846 } 1847 1848 ////////////////////////////////////////////////////////////////////////////////// 1849 // DXRenderModule::screenshot 1850 ////////////////////////////////////////////////////////////////////////////////// 1851 1852 void DXRenderModule::screenShot() 1853 { 1854 if(!(mpBackBufferSurface.get())) 1855 return; 1856 char filename[256]; 1857 static sal_uInt32 counter = 0; 1858 sprintf(filename,"c:\\shot%d.bmp",counter++); 1859 dumpSurface(mpBackBufferSurface,filename); 1860 } 1861 1862 ////////////////////////////////////////////////////////////////////////////////// 1863 // DXRenderModule::validateMainSurfaces 1864 ////////////////////////////////////////////////////////////////////////////////// 1865 1866 bool DXRenderModule::validateMainSurfaces() 1867 { 1868 if(mpPrimarySurface.get()) { 1869 if(mpPrimarySurface->IsLost() == DDERR_SURFACELOST) { 1870 if(FAILED(mpPrimarySurface->Restore())) 1871 return false; 1872 } 1873 } 1874 1875 if(mpBackBufferSurface.get()) { 1876 if(mpBackBufferSurface->IsLost() == DDERR_SURFACELOST) 1877 { 1878 // TODO(F1): simply restoring the backbuffer does not 1879 // work as expected, we need to re-create everything 1880 // from scratch. find out why... 1881 //if(SUCCEEDED(mpBackBufferSurface->Restore())) 1882 // return setup3DDevice(); 1883 1884 mpBackBufferSurface.reset(); 1885 1886 // get us a backbuffer for simulated flipping 1887 IDirectDrawSurface* pSurface; 1888 1889 // TODO(P2): Strictly speaking, we don't need a full screen worth of 1890 // backbuffer here. We could also scale dynamically with the current 1891 // window size, but this will make it necessary to temporarily have two 1892 // buffers while copying from the old to the new one. YMMV. 1893 const ::basegfx::B2ISize aSize( getFramebufferSize() ); 1894 1895 DDSURFACEDESC aSurfaceDesc; 1896 rtl_fillMemory( &aSurfaceDesc, sizeof(DDSURFACEDESC), 0 ); 1897 aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC); 1898 aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; 1899 aSurfaceDesc.dwHeight= aSize.getY(); 1900 aSurfaceDesc.dwWidth = aSize.getX(); 1901 1902 aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; 1903 1904 HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL); 1905 1906 if( FAILED( nRes ) ) 1907 { 1908 if( nRes == DDERR_OUTOFVIDEOMEMORY ) 1909 { 1910 // local vid mem failed. Maybe AGP mem works? 1911 aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; 1912 if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL)) ) 1913 { 1914 // no chance 1915 return false; 1916 } 1917 1918 VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer reverted to non-local video mem" ); 1919 } 1920 else 1921 { 1922 // no chance 1923 VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" ); 1924 return false; 1925 } 1926 } 1927 1928 VERBOSE_TRACE( "Device::Device(): created backbuffer of size %d times %d pixel", 1929 aSurfaceDesc.dwWidth, 1930 aSurfaceDesc.dwHeight ); 1931 1932 mpBackBufferSurface = COMReference< IDirectDrawSurface >(pSurface); 1933 1934 return setup3DDevice(); 1935 } 1936 } 1937 1938 return true; 1939 } 1940 1941 void DXRenderModule::renderInfoText( const ::rtl::OUString& rStr, 1942 const Gdiplus::PointF& rPos ) const 1943 { 1944 ENSURE_OR_THROW( !mnBeginSceneCount, 1945 "Device::renderInfoText(): within 3D scene" ); 1946 1947 // render text directly to primary surface 1948 GraphicsSharedPtr pGraphics; 1949 1950 if( mbPageFlipping ) 1951 { 1952 // render on top of backbuffer. We have 1953 // page flipping, anyway, thus this will 1954 // cost us nothing. 1955 pGraphics = createSurfaceGraphics( mpBackBufferSurface ); 1956 } 1957 else 1958 { 1959 // render FPS directly to front buffer. 1960 // That saves us another explicit blit, 1961 // and for me, the FPS counter can blink, 1962 // if it likes to... 1963 pGraphics = createSurfaceGraphics( mpPrimarySurface ); 1964 } 1965 1966 if( !mbPageFlipping ) 1967 { 1968 // clear background. We might be doing optimized redraws, 1969 // and the background under the FPS count will then not be 1970 // cleared. 1971 Gdiplus::SolidBrush aBrush( 1972 Gdiplus::Color( 255, 255, 255 ) ); 1973 1974 pGraphics->FillRectangle( &aBrush, 1975 rPos.X, rPos.Y, 80.0, 20.0 ); 1976 } 1977 1978 Gdiplus::SolidBrush aBrush( 1979 Gdiplus::Color( 255, 0, 255 ) ); 1980 Gdiplus::Font aFont( NULL, 1981 16, 1982 Gdiplus::FontStyleRegular, 1983 Gdiplus::UnitWorld, 1984 NULL ); 1985 pGraphics->DrawString( reinterpret_cast<LPCWSTR>(rStr.getStr()), 1986 rStr.getLength(), 1987 &aFont, 1988 rPos, 1989 &aBrush ); 1990 } 1991 1992 ////////////////////////////////////////////////////////////////////////////////// 1993 // DXRenderModule::renderMemAvailable 1994 ////////////////////////////////////////////////////////////////////////////////// 1995 1996 void DXRenderModule::renderMemAvailable() const 1997 { 1998 ENSURE_OR_THROW( !mnBeginSceneCount, 1999 "DXRenderModule::renderMemAvailable(): within 3D scene" ); 2000 2001 const double nSurfaceMem( getAvailableSurfaceMem()/1024 ); 2002 2003 ::rtl::OUString text( ::rtl::math::doubleToUString( nSurfaceMem, 2004 rtl_math_StringFormat_F, 2005 2,'.',NULL,' ') ); 2006 2007 // pad with leading space 2008 while( text.getLength() < 6 ) 2009 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 2010 2011 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("S: ")) + text; 2012 2013 renderInfoText( text, 2014 Gdiplus::PointF( 0.0, 20) ); 2015 2016 2017 const double nTexMem( getAvailableTextureMem()/1024 ); 2018 2019 text = ::rtl::math::doubleToUString( nTexMem, 2020 rtl_math_StringFormat_F, 2021 2,'.',NULL,' '); 2022 // pad with leading space 2023 while( text.getLength() < 6 ) 2024 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 2025 2026 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("T: ")) + text; 2027 2028 renderInfoText( text, 2029 Gdiplus::PointF( 0.0, 40) ); 2030 2031 VERBOSE_TRACE( "dxcanvas: %f free surface mem, %f free texture mem", 2032 nSurfaceMem, nTexMem ); 2033 } 2034 2035 ////////////////////////////////////////////////////////////////////////////////// 2036 // DXRenderModule::renderFPSCounter 2037 ////////////////////////////////////////////////////////////////////////////////// 2038 2039 void DXRenderModule::renderFPSCounter() const 2040 { 2041 ENSURE_OR_THROW( !mnBeginSceneCount, 2042 "DXRenderModule::ren derFPSCounter(): within 3D scene" ); 2043 2044 const double denominator( maLastUpdate.getElapsedTime() ); 2045 maLastUpdate.reset(); 2046 2047 ::rtl::OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator, 2048 rtl_math_StringFormat_F, 2049 2,'.',NULL,' ') ); 2050 2051 // pad with leading space 2052 while( text.getLength() < 6 ) 2053 text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text; 2054 2055 text += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" fps")); 2056 2057 renderInfoText( text, 2058 Gdiplus::PointF() ); 2059 2060 VERBOSE_TRACE( "dxcanvas: %f FPS", 2061 denominator == 0.0 ? 100.0 : 1.0/denominator ); 2062 } 2063 2064 ////////////////////////////////////////////////////////////////////////////////// 2065 // DXRenderModule::resize 2066 ////////////////////////////////////////////////////////////////////////////////// 2067 2068 void DXRenderModule::resize( const ::basegfx::B2IRange& rect ) 2069 { 2070 // TODO(P2): get rid of those fine-grained locking 2071 ::osl::MutexGuard aGuard( maMutex ); 2072 2073 if( mhWnd==0 ) 2074 return; 2075 2076 // don't do anything if the size didn't change. 2077 if(maSize.getX() == static_cast<sal_Int32>(rect.getWidth()) && 2078 maSize.getY() == static_cast<sal_Int32>(rect.getHeight())) 2079 return; 2080 2081 // TODO(Q2): use numeric cast to prevent overflow 2082 maSize.setX(static_cast<sal_Int32>(rect.getWidth())); 2083 maSize.setY(static_cast<sal_Int32>(rect.getHeight())); 2084 2085 mpWindow->SetPosSizePixel(0,0,maSize.getX(),maSize.getY()); 2086 } 2087 2088 ////////////////////////////////////////////////////////////////////////////////// 2089 // DXRenderModule::getPageSize 2090 ////////////////////////////////////////////////////////////////////////////////// 2091 2092 ::basegfx::B2IVector DXRenderModule::getPageSize() 2093 { 2094 // TODO(P2): get rid of those fine-grained locking 2095 ::osl::MutexGuard aGuard( maMutex ); 2096 return maPageSize; 2097 } 2098 2099 ::canvas::ISurfaceSharedPtr DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize ) 2100 { 2101 // TODO(P2): get rid of those fine-grained locking 2102 ::osl::MutexGuard aGuard( maMutex ); 2103 2104 const ::basegfx::B2IVector& rPageSize( getPageSize() ); 2105 ::basegfx::B2ISize aSize(surfaceSize); 2106 if(!(aSize.getX())) 2107 aSize.setX(rPageSize.getX()); 2108 if(!(aSize.getY())) 2109 aSize.setY(rPageSize.getY()); 2110 2111 if(mpTexture.use_count() == 1) 2112 return mpTexture; 2113 2114 return ::canvas::ISurfaceSharedPtr( 2115 new DXSurface(*this, 2116 aSize) ); 2117 } 2118 2119 void DXRenderModule::beginPrimitive( PrimitiveType eType ) 2120 { 2121 // TODO(P2): get rid of those fine-grained locking 2122 ::osl::MutexGuard aGuard( maMutex ); 2123 2124 ENSURE_OR_THROW( !mnBeginSceneCount, 2125 "DXRenderModule::beginPrimitive(): nested call" ); 2126 2127 ++mnBeginSceneCount; 2128 meType=eType; 2129 mnCount=0; 2130 } 2131 2132 void DXRenderModule::endPrimitive() 2133 { 2134 // TODO(P2): get rid of those fine-grained locking 2135 ::osl::MutexGuard aGuard( maMutex ); 2136 2137 --mnBeginSceneCount; 2138 meType=PRIMITIVE_TYPE_UNKNOWN; 2139 mnCount=0; 2140 } 2141 2142 void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex ) 2143 { 2144 // TODO(P2): get rid of those fine-grained locking 2145 ::osl::MutexGuard aGuard( maMutex ); 2146 2147 switch(meType) 2148 { 2149 case PRIMITIVE_TYPE_TRIANGLE: 2150 { 2151 maVertexCache.push_back(vertex); 2152 ++mnCount; 2153 mnCount &= 3; 2154 break; 2155 } 2156 2157 case PRIMITIVE_TYPE_QUAD: 2158 { 2159 if(mnCount == 3) 2160 { 2161 const std::size_t size(maVertexCache.size()); 2162 ::canvas::Vertex v0(maVertexCache[size-1]); 2163 ::canvas::Vertex v2(maVertexCache[size-3]); 2164 maVertexCache.push_back(v0); 2165 maVertexCache.push_back(vertex); 2166 maVertexCache.push_back(v2); 2167 mnCount=0; 2168 } 2169 else 2170 { 2171 maVertexCache.push_back(vertex); 2172 ++mnCount; 2173 } 2174 break; 2175 } 2176 2177 default: 2178 OSL_ENSURE( false, 2179 "DXRenderModule::pushVertex(): unexpected primitive types" ); 2180 break; 2181 } 2182 } 2183 2184 bool DXRenderModule::isError() 2185 { 2186 // TODO(P2): get rid of those fine-grained locking 2187 ::osl::MutexGuard aGuard( maMutex ); 2188 2189 return mbError; 2190 } 2191 2192 void DXRenderModule::flushVertexCache() 2193 { 2194 if(!(maVertexCache.size())) 2195 return; 2196 2197 mbError=true; 2198 2199 if( FAILED(mpDirect3DDevice->BeginScene()) ) 2200 return; 2201 2202 // enable texture alpha blending 2203 if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE))) 2204 return; 2205 2206 // enable texture alpha modulation, for honoring fAlpha 2207 if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND, 2208 D3DTBLEND_MODULATEALPHA)) ) 2209 return; 2210 2211 // enable texture magnification filtering (don't care if this 2212 // fails, it's just visually more pleasant) 2213 mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAG, 2214 D3DFILTER_LINEAR); 2215 2216 // enable texture minification filtering (don't care if this 2217 // fails, it's just visually more pleasant) 2218 mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMIN, 2219 D3DFILTER_LINEAR); 2220 2221 // enable subpixel texture output (don't care if this 2222 // fails, it's just visually more pleasant) 2223 mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_SUBPIXEL, 2224 TRUE); 2225 2226 // normal combination of object... 2227 if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, 2228 D3DBLEND_SRCALPHA)) ) 2229 return; 2230 2231 // ..and background color 2232 if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, 2233 D3DBLEND_INVSRCALPHA)) ) 2234 return; 2235 2236 // disable backface culling; this enables us to mirror sprites 2237 // by simply reverting the triangles, which, with enabled 2238 // culling, would be invisible otherwise 2239 if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, 2240 D3DCULL_NONE)) ) 2241 return; 2242 2243 mbError=false; 2244 2245 const float nHalfPixelSizeX(0.5f/maPageSize.getX()); 2246 const float nHalfPixelSizeY(0.5f/maPageSize.getY()); 2247 sal_uInt32 nIndex(0); 2248 const std::size_t size(maVertexCache.size()); 2249 D3DTLVERTEX *vertices = static_cast<D3DTLVERTEX *>(_alloca(sizeof(D3DTLVERTEX)*size)); 2250 vertexCache_t::const_iterator it(maVertexCache.begin()); 2251 while(it != maVertexCache.end()) 2252 { 2253 vertices[nIndex++] = D3DTLVERTEX( 2254 D3DVECTOR(static_cast<D3DVALUE>(it->x), 2255 static_cast<D3DVALUE>(it->y), 2256 static_cast<D3DVALUE>(it->z)), 2257 1, 2258 D3DRGBA(1,1,1,it->a), 2259 D3DRGBA(0,0,0,0), 2260 static_cast<float>(it->u + nHalfPixelSizeX), 2261 static_cast<float>(it->v + nHalfPixelSizeY)); 2262 ++it; 2263 } 2264 2265 maVertexCache.clear(); 2266 2267 mbError |= FAILED(mpDirect3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 2268 D3DVT_TLVERTEX, 2269 (LPVOID)vertices, 2270 size, 2271 0)); 2272 2273 mbError |= FAILED(mpDirect3DDevice->EndScene()); 2274 } 2275 } 2276 2277 IDXRenderModuleSharedPtr createRenderModule( const ::Window& rParent ) 2278 { 2279 return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) ); 2280 } 2281 } 2282 2283 #endif 2284