1 // Created on: 2015-06-10
2 // Created by: Kirill Gavrilov
3 // Copyright (c) 2015 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15 
16 #include <d3d9.h>
17 
18 #include <D3DHost_View.hxx>
19 
20 #include <D3DHost_GraphicDriver.hxx>
21 #include <TCollection_ExtendedString.hxx>
22 #include <OpenGl_Window.hxx>
23 
24 #include <Standard_WarningDisableFunctionCast.hxx>
25 
26 IMPLEMENT_STANDARD_RTTIEXT(D3DHost_View,OpenGl_View)
27 
28 namespace
29 {
30   enum D3DHost_VendorId
31   {
32     D3DHost_VendorId_AMD    = 0x1002,
33     D3DHost_VendorId_NVIDIA = 0x10DE,
34     D3DHost_VendorId_Intel  = 0x8086,
35   };
36 }
37 
38 // =======================================================================
39 // function : d3dFormatError
40 // purpose  :
41 // =======================================================================
d3dFormatError(const long theErrCode)42 TCollection_AsciiString D3DHost_View::d3dFormatError (const long theErrCode)
43 {
44   switch (theErrCode)
45   {
46     case D3D_OK:                     return "OK";
47     case D3DERR_DEVICELOST:          return "Device lost";
48     case D3DERR_DEVICEREMOVED:       return "Device removed";
49     case D3DERR_DRIVERINTERNALERROR: return "Driver internal error";
50     case D3DERR_OUTOFVIDEOMEMORY:    return "Out of video memory";
51     case D3DERR_INVALIDCALL:         return "Invalid call";
52     default:                         return TCollection_AsciiString ("Error #") + int(theErrCode) + ")";
53   }
54 }
55 
56 // =======================================================================
57 // function : D3DHost_View
58 // purpose  :
59 // =======================================================================
D3DHost_View(const Handle (Graphic3d_StructureManager)& theMgr,const Handle (D3DHost_GraphicDriver)& theDriver,const Handle (OpenGl_Caps)& theCaps,OpenGl_StateCounter * theCounter)60 D3DHost_View::D3DHost_View (const Handle(Graphic3d_StructureManager)& theMgr,
61                             const Handle(D3DHost_GraphicDriver)& theDriver,
62                             const Handle(OpenGl_Caps)& theCaps,
63                             OpenGl_StateCounter* theCounter)
64 : OpenGl_View (theMgr, theDriver, theCaps, theCounter),
65   myD3dLib      (NULL),
66   myD3dDevice   (NULL),
67   myD3dParams   (new D3DPRESENT_PARAMETERS()),
68   myRefreshRate (D3DPRESENT_RATE_DEFAULT),
69   myIsD3dEx     (false)
70 {
71   memset(myD3dParams.operator->(), 0, sizeof(D3DPRESENT_PARAMETERS));
72 
73   myD3dParams->Windowed         = TRUE;
74   myD3dParams->SwapEffect       = D3DSWAPEFFECT_DISCARD;
75   myD3dParams->BackBufferFormat = D3DFMT_X8R8G8B8;
76   myD3dParams->BackBufferCount  = 1;
77   myD3dParams->BackBufferHeight = 2;
78   myD3dParams->BackBufferWidth  = 2;
79   myD3dParams->EnableAutoDepthStencil     = FALSE;
80   myD3dParams->AutoDepthStencilFormat     = D3DFMT_D16_LOCKABLE;
81   myD3dParams->FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
82   myD3dParams->PresentationInterval       = D3DPRESENT_INTERVAL_DEFAULT;
83 }
84 
85 // =======================================================================
86 // function : ~D3DHost_View
87 // purpose  :
88 // =======================================================================
~D3DHost_View()89 D3DHost_View::~D3DHost_View()
90 {
91   ReleaseGlResources (NULL);
92   if (myD3dDevice != NULL)
93   {
94     myD3dDevice->Release();
95     myD3dDevice = NULL;
96   }
97   if (myD3dLib != NULL)
98   {
99     myD3dLib->Release();
100     myD3dLib = NULL;
101   }
102 }
103 
104 // =======================================================================
105 // function : ReleaseGlResources
106 // purpose  :
107 // =======================================================================
ReleaseGlResources(const Handle (OpenGl_Context)& theCtx)108 void D3DHost_View::ReleaseGlResources (const Handle(OpenGl_Context)& theCtx)
109 {
110   if (!myD3dWglFbo.IsNull())
111   {
112     myD3dWglFbo->Release (theCtx.get());
113     myD3dWglFbo.Nullify();
114   }
115   OpenGl_View::ReleaseGlResources (theCtx);
116 }
117 
118 // =======================================================================
119 // function : D3dColorSurface
120 // purpose  :
121 // =======================================================================
D3dColorSurface() const122 IDirect3DSurface9* D3DHost_View::D3dColorSurface() const
123 {
124   return myD3dWglFbo->D3dColorSurface();
125 }
126 
127 // =======================================================================
128 // function : SetWindow
129 // purpose  :
130 // =======================================================================
SetWindow(const Handle (Aspect_Window)& theWindow,const Aspect_RenderingContext theContext)131 void D3DHost_View::SetWindow (const Handle(Aspect_Window)& theWindow,
132                               const Aspect_RenderingContext theContext)
133 {
134   if (!myD3dWglFbo.IsNull())
135   {
136     myD3dWglFbo->Release (myWorkspace->GetGlContext().operator->());
137     myD3dWglFbo.Nullify();
138   }
139   if (myD3dDevice != NULL)
140   {
141     myD3dDevice->Release();
142     myD3dDevice = NULL;
143   }
144 
145   OpenGl_View::SetWindow (theWindow, theContext);
146 
147   if (!myWindow.IsNull())
148   {
149     d3dInit();
150     d3dCreateRenderTarget();
151   }
152 }
153 
154 // =======================================================================
155 // function : DiagnosticInformation
156 // purpose  :
157 // =======================================================================
DiagnosticInformation(TColStd_IndexedDataMapOfStringString & theDict,Graphic3d_DiagnosticInfo theFlags) const158 void D3DHost_View::DiagnosticInformation (TColStd_IndexedDataMapOfStringString& theDict,
159                                           Graphic3d_DiagnosticInfo theFlags) const
160 {
161   base_type::DiagnosticInformation (theDict, theFlags);
162   if (myD3dDevice == NULL)
163   {
164     return;
165   }
166 
167   D3DCAPS9 aDevCaps;
168   memset (&aDevCaps, 0, sizeof(aDevCaps));
169   if (myD3dDevice->GetDeviceCaps (&aDevCaps) < 0)
170   {
171     return;
172   }
173 
174   const UINT anAdapter = aDevCaps.AdapterOrdinal;
175   D3DADAPTER_IDENTIFIER9 aDevId;
176   memset (&aDevId, 0, sizeof(aDevId));
177   if (myD3dLib->GetAdapterIdentifier (anAdapter, 0, &aDevId) < 0)
178   {
179     return;
180   }
181 
182   TCollection_AsciiString aVendorId ((int )aDevId.VendorId);
183   switch (aDevId.VendorId)
184   {
185     case D3DHost_VendorId_AMD:    aVendorId = "AMD";    break;
186     case D3DHost_VendorId_NVIDIA: aVendorId = "NVIDIA"; break;
187     case D3DHost_VendorId_Intel:  aVendorId = "Intel";  break;
188   }
189   theDict.Add ("D3Dvendor",      aVendorId);
190   theDict.Add ("D3Ddescription", aDevId.Description);
191   theDict.Add ("D3DdeviceName",  aDevId.DeviceName);
192   theDict.Add ("D3Ddriver",      aDevId.Driver);
193   theDict.Add ("D3DdeviceId",    TCollection_AsciiString((int )aDevId.DeviceId));
194   theDict.Add ("D3Dinterop",     myD3dWglFbo.IsNull() || myD3dWglFbo->D3dFallback()
195                                ? "Software Fallback"
196                                : "WGL_NV_DX_interop");
197 }
198 
199 // =======================================================================
200 // function : d3dInitLib
201 // purpose  :
202 // =======================================================================
d3dInitLib()203 bool D3DHost_View::d3dInitLib()
204 {
205   if (myD3dLib == NULL)
206   {
207     IDirect3D9Ex* aD3dLibEx = NULL;
208     // we link against d3d (using Direct3DCreate9 symbol), thus it should be already loaded
209     HMODULE aLib = GetModuleHandleW (L"d3d9");
210     if (aLib != NULL)
211     {
212       // retrieve D3D9Ex function dynamically (available only since Vista+)
213       typedef HRESULT (WINAPI* Direct3DCreate9Ex_t)(UINT , IDirect3D9Ex** );
214       Direct3DCreate9Ex_t Direct3DCreate9ExProc = (Direct3DCreate9Ex_t )GetProcAddress (aLib, "Direct3DCreate9Ex");
215       if (Direct3DCreate9ExProc != NULL)
216       {
217         Direct3DCreate9ExProc(D3D_SDK_VERSION, &aD3dLibEx);
218       }
219     }
220     myD3dLib  = aD3dLibEx;
221     myIsD3dEx = aD3dLibEx != NULL;
222     if (myD3dLib == NULL)
223     {
224       myD3dLib = Direct3DCreate9 (D3D_SDK_VERSION);
225     }
226   }
227   return myD3dLib != NULL;
228 }
229 
230 // =======================================================================
231 // function : d3dInit
232 // purpose  :
233 // =======================================================================
d3dInit()234 bool D3DHost_View::d3dInit()
235 {
236   if (!d3dInitLib())
237   {
238     myWorkspace->GetGlContext()->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, "Direct3DCreate9 failed!");
239     return false;
240   }
241 
242   UINT anAdapterId = D3DADAPTER_DEFAULT;
243 
244   // setup the present parameters
245   D3DDISPLAYMODE aCurrMode;
246   memset(&aCurrMode, 0, sizeof(aCurrMode));
247   if (myD3dLib->GetAdapterDisplayMode (anAdapterId, &aCurrMode) == D3D_OK)
248   {
249     myD3dParams->BackBufferFormat = aCurrMode.Format;
250     myRefreshRate = aCurrMode.RefreshRate;
251   }
252   myD3dParams->Windowed         = TRUE;
253   myD3dParams->BackBufferWidth  = myWindow->Width();
254   myD3dParams->BackBufferHeight = myWindow->Height();
255   myD3dParams->hDeviceWindow    = (HWND )myWindow->PlatformWindow()->NativeHandle();
256 
257   // create the Video Device
258   HRESULT isOK = myD3dLib->CreateDevice (anAdapterId, D3DDEVTYPE_HAL,
259                                          (HWND )myWindow->PlatformWindow()->NativeHandle(),
260                                          D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
261                                          myD3dParams.get(), &myD3dDevice);
262   if (isOK < 0)
263   {
264     return false;
265   }
266 
267   return myD3dDevice != NULL;
268 }
269 
270 // =======================================================================
271 // function : d3dReset
272 // purpose  :
273 // =======================================================================
d3dReset()274 bool D3DHost_View::d3dReset()
275 {
276   if (myD3dDevice == NULL)
277   {
278     return false;
279   }
280 
281   myD3dParams->Windowed         = TRUE;
282   myD3dParams->BackBufferWidth  = myWindow->Width();
283   myD3dParams->BackBufferHeight = myWindow->Height();
284   myD3dParams->hDeviceWindow    = (HWND )myWindow->PlatformWindow()->NativeHandle();
285   myD3dParams->FullScreen_RefreshRateInHz = !myD3dParams->Windowed ? myRefreshRate : 0;
286 
287   HRESULT isOK = myD3dDevice->Reset(myD3dParams.get());
288   return isOK == D3D_OK;
289 }
290 
291 // =======================================================================
292 // function : d3dCreateRenderTarget
293 // purpose  :
294 // =======================================================================
d3dCreateRenderTarget()295 bool D3DHost_View::d3dCreateRenderTarget()
296 {
297   bool toD3dFallback = false;
298   if (myD3dWglFbo.IsNull())
299   {
300     myD3dWglFbo = new D3DHost_FrameBuffer();
301   }
302   else
303   {
304     toD3dFallback = myD3dWglFbo->D3dFallback();
305   }
306 
307   if (!toD3dFallback)
308   {
309     toD3dFallback = !myD3dWglFbo->InitD3dInterop (myWorkspace->GetGlContext(),
310                                                   myD3dDevice,
311                                                   myIsD3dEx,
312                                                   myWindow->Width(),
313                                                   myWindow->Height(),
314                                                   0); // do not request depth-stencil attachment since buffer will be flipped using addition FBO (myToFlipOutput)
315   }
316   if (toD3dFallback)
317   {
318     if (!myD3dWglFbo->InitD3dFallback (myWorkspace->GetGlContext(),
319                                        myD3dDevice,
320                                        myIsD3dEx,
321                                        myWindow->Width(),
322                                        myWindow->Height(),
323                                        GL_DEPTH24_STENCIL8))
324     {
325       return false;
326     }
327   }
328 
329   myD3dDevice->SetRenderTarget (0, myD3dWglFbo->D3dColorSurface());
330   return true;
331 }
332 
333 // =======================================================================
334 // function : d3dBeginRender
335 // purpose  :
336 // =======================================================================
d3dBeginRender()337 void D3DHost_View::d3dBeginRender()
338 {
339   if (myD3dDevice == NULL)
340   {
341     return;
342   }
343 
344   // clear the back buffer
345   myD3dDevice->Clear (0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
346   myD3dDevice->BeginScene();
347 }
348 
349 // =======================================================================
350 // function : d3dEndRender
351 // purpose  :
352 // =======================================================================
d3dEndRender()353 void D3DHost_View::d3dEndRender()
354 {
355   if (myD3dDevice != NULL)
356   {
357     myD3dDevice->EndScene();
358   }
359 }
360 
361 // =======================================================================
362 // function : d3dSwap
363 // purpose  :
364 // =======================================================================
d3dSwap()365 bool D3DHost_View::d3dSwap()
366 {
367   if (myD3dDevice == NULL)
368   {
369     return false;
370   }
371 
372   const HRESULT isOK = myD3dDevice->Present (NULL, NULL, NULL, NULL);
373   if (isOK != D3D_OK)
374   {
375     myWorkspace->GetGlContext()->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
376                                               TCollection_AsciiString("Direct3D9, Present device failed, ") + d3dFormatError (isOK));
377   }
378   return isOK == D3D_OK;
379 }
380 
381 // =======================================================================
382 // function : Redraw
383 // purpose  :
384 // =======================================================================
Redraw()385 void D3DHost_View::Redraw()
386 {
387   if (!myWorkspace->Activate()
388     || myD3dDevice == NULL)
389   {
390     return;
391   }
392   else if (!myFBO.IsNull())
393   {
394     OpenGl_View::Redraw();
395     return;
396   }
397 
398   Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext();
399   if (myWindow->PlatformWindow()->IsVirtual()
400   &&  aCtx->arbFBO == NULL)
401   {
402     // do a dirty hack in extreme fallback mode with OpenGL driver not supporting FBO,
403     // the back buffer of hidden window is used for rendering as offscreen buffer
404     myTransientDrawToFront = false;
405     int aWinSizeX = 0, aWinSizeY = 0;
406     myWindow->PlatformWindow()->Size (aWinSizeX, aWinSizeY);
407     WINDOWPLACEMENT aPlace;
408     GetWindowPlacement ((HWND )myWindow->PlatformWindow()->NativeHandle(), &aPlace);
409     if (aPlace.rcNormalPosition.right  - aPlace.rcNormalPosition.left != aWinSizeX
410      || aPlace.rcNormalPosition.bottom - aPlace.rcNormalPosition.top  != aWinSizeY)
411     {
412       aPlace.rcNormalPosition.right  = aPlace.rcNormalPosition.left + aWinSizeX;
413       aPlace.rcNormalPosition.bottom = aPlace.rcNormalPosition.top  + aWinSizeY;
414       aPlace.showCmd = SW_HIDE;
415       SetWindowPlacement ((HWND )myWindow->PlatformWindow()->NativeHandle(), &aPlace);
416     }
417   }
418 
419   myD3dWglFbo->LockSurface   (aCtx);
420   if (myD3dWglFbo->IsValid())
421   {
422     myToFlipOutput = Standard_True;
423     myFBO = myD3dWglFbo;
424   }
425   OpenGl_View::Redraw();
426   myFBO.Nullify();
427   myD3dWglFbo->UnlockSurface (aCtx);
428   myToFlipOutput = Standard_False;
429   if (aCtx->caps->buffersNoSwap)
430   {
431     return;
432   }
433 
434   // blit result to the D3D back buffer and swap
435   d3dBeginRender();
436 
437   IDirect3DSurface9* aBackbuffer = NULL;
438   myD3dDevice->GetBackBuffer (0, 0, D3DBACKBUFFER_TYPE_MONO, &aBackbuffer);
439   myD3dDevice->StretchRect (myD3dWglFbo->D3dColorSurface(), NULL, aBackbuffer, NULL, D3DTEXF_LINEAR);
440   aBackbuffer->Release();
441 
442   d3dEndRender();
443   d3dSwap();
444 }
445 
446 // =======================================================================
447 // function : RedrawImmediate
448 // purpose  :
449 // =======================================================================
RedrawImmediate()450 void D3DHost_View::RedrawImmediate()
451 {
452   Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext();
453   if (!myTransientDrawToFront
454    || !myBackBufferRestored
455    || (aCtx->caps->buffersNoSwap && !myMainSceneFbos[0]->IsValid()))
456   {
457     Redraw();
458     return;
459   }
460   else if (!myWorkspace->Activate()
461          || myD3dDevice == NULL)
462   {
463     return;
464   }
465   else if (!myFBO.IsNull())
466   {
467     OpenGl_View::Redraw();
468     return;
469   }
470 
471   myD3dWglFbo->LockSurface   (aCtx);
472   if (myD3dWglFbo->IsValid())
473   {
474     myToFlipOutput = Standard_True;
475     myFBO = myD3dWglFbo;
476   }
477   OpenGl_View::RedrawImmediate();
478   myFBO.Nullify();
479   myD3dWglFbo->UnlockSurface (aCtx);
480   myToFlipOutput = Standard_False;
481   if (aCtx->caps->buffersNoSwap)
482   {
483     return;
484   }
485 
486   // blit result to the D3D back buffer and swap
487   d3dBeginRender();
488 
489   IDirect3DSurface9* aBackbuffer = NULL;
490   myD3dDevice->GetBackBuffer (0, 0, D3DBACKBUFFER_TYPE_MONO, &aBackbuffer);
491   myD3dDevice->StretchRect (myD3dWglFbo->D3dColorSurface(), NULL, aBackbuffer, NULL, D3DTEXF_LINEAR);
492   aBackbuffer->Release();
493 
494   d3dEndRender();
495   d3dSwap();
496 }
497 
498 // =======================================================================
499 // function : Resize
500 // purpose  :
501 // =======================================================================
Resized()502 void D3DHost_View::Resized()
503 {
504   const Standard_Integer aWidthOld  = myWindow->Width();
505   const Standard_Integer aHeightOld = myWindow->Height();
506   OpenGl_View::Resized();
507   if (aWidthOld  == myWindow->Width()
508    && aHeightOld == myWindow->Height())
509   {
510     return;
511   }
512 
513   if (!myD3dWglFbo.IsNull())
514   {
515     myD3dWglFbo->Release (myWorkspace->GetGlContext().operator->());
516   }
517   if (!myWorkspace->GetGlContext()->caps->buffersNoSwap)
518   {
519     d3dReset();
520   }
521   d3dCreateRenderTarget();
522 }
523