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