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