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