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