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