62e1beed |
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 | |
d2eddacc |
16 | #include <d3d9.h> |
17 | |
c357e426 |
18 | #include <D3DHost_View.hxx> |
62e1beed |
19 | |
c357e426 |
20 | #include <D3DHost_GraphicDriver.hxx> |
62e1beed |
21 | #include <TCollection_ExtendedString.hxx> |
22 | |
f4a7308f |
23 | #include <Standard_WarningDisableFunctionCast.hxx> |
24 | |
92efcf78 |
25 | IMPLEMENT_STANDARD_RTTIEXT(D3DHost_View,OpenGl_View) |
26 | |
dc9f1dbf |
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 | |
62e1beed |
37 | // ======================================================================= |
38 | // function : d3dFormatError |
39 | // purpose : |
40 | // ======================================================================= |
d2eddacc |
41 | TCollection_AsciiString D3DHost_View::d3dFormatError (const long theErrCode) |
62e1beed |
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 | // ======================================================================= |
c357e426 |
56 | // function : D3DHost_View |
62e1beed |
57 | // purpose : |
58 | // ======================================================================= |
c357e426 |
59 | D3DHost_View::D3DHost_View (const Handle(Graphic3d_StructureManager)& theMgr, |
60 | const Handle(D3DHost_GraphicDriver)& theDriver, |
61 | const Handle(OpenGl_Caps)& theCaps, |
c357e426 |
62 | OpenGl_StateCounter* theCounter) |
851dacdb |
63 | : OpenGl_View (theMgr, theDriver, theCaps, theCounter), |
62e1beed |
64 | myD3dLib (NULL), |
65 | myD3dDevice (NULL), |
d2eddacc |
66 | myD3dParams (new D3DPRESENT_PARAMETERS()), |
62e1beed |
67 | myRefreshRate (D3DPRESENT_RATE_DEFAULT), |
68 | myIsD3dEx (false) |
69 | { |
d2eddacc |
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; |
62e1beed |
82 | } |
83 | |
84 | // ======================================================================= |
c357e426 |
85 | // function : ~D3DHost_View |
62e1beed |
86 | // purpose : |
87 | // ======================================================================= |
c357e426 |
88 | D3DHost_View::~D3DHost_View() |
62e1beed |
89 | { |
90 | if (!myD3dWglFbo.IsNull()) |
91 | { |
c357e426 |
92 | myD3dWglFbo->Release (myWorkspace->GetGlContext().operator->()); |
62e1beed |
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 | |
6cde53c4 |
107 | // ======================================================================= |
108 | // function : D3dColorSurface |
109 | // purpose : |
110 | // ======================================================================= |
111 | IDirect3DSurface9* D3DHost_View::D3dColorSurface() const |
112 | { |
113 | return myD3dWglFbo->D3dColorSurface(); |
114 | } |
115 | |
c357e426 |
116 | // ======================================================================= |
117 | // function : SetWindow |
118 | // purpose : |
119 | // ======================================================================= |
120 | void D3DHost_View::SetWindow (const Handle(Aspect_Window)& theWindow, |
7aa74f30 |
121 | const Aspect_RenderingContext theContext) |
c357e426 |
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 | |
7aa74f30 |
134 | OpenGl_View::SetWindow (theWindow, theContext); |
c357e426 |
135 | |
136 | if (!myWindow.IsNull()) |
137 | { |
138 | d3dInit(); |
139 | d3dCreateRenderTarget(); |
140 | } |
141 | } |
142 | |
dc9f1dbf |
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 | |
62e1beed |
188 | // ======================================================================= |
189 | // function : d3dInitLib |
190 | // purpose : |
191 | // ======================================================================= |
c357e426 |
192 | bool D3DHost_View::d3dInitLib() |
62e1beed |
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 | // ======================================================================= |
c357e426 |
223 | bool D3DHost_View::d3dInit() |
62e1beed |
224 | { |
225 | if (!d3dInitLib()) |
226 | { |
6cde53c4 |
227 | myWorkspace->GetGlContext()->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, "Direct3DCreate9 failed!"); |
62e1beed |
228 | return false; |
229 | } |
230 | |
231 | UINT anAdapterId = D3DADAPTER_DEFAULT; |
232 | |
233 | // setup the present parameters |
d2eddacc |
234 | D3DDISPLAYMODE aCurrMode; |
235 | memset(&aCurrMode, 0, sizeof(aCurrMode)); |
236 | if (myD3dLib->GetAdapterDisplayMode (anAdapterId, &aCurrMode) == D3D_OK) |
62e1beed |
237 | { |
d2eddacc |
238 | myD3dParams->BackBufferFormat = aCurrMode.Format; |
239 | myRefreshRate = aCurrMode.RefreshRate; |
62e1beed |
240 | } |
d2eddacc |
241 | myD3dParams->Windowed = TRUE; |
242 | myD3dParams->BackBufferWidth = myWindow->Width(); |
243 | myD3dParams->BackBufferHeight = myWindow->Height(); |
244 | myD3dParams->hDeviceWindow = (HWND )myWindow->PlatformWindow()->NativeHandle(); |
62e1beed |
245 | |
246 | // create the Video Device |
247 | HRESULT isOK = myD3dLib->CreateDevice (anAdapterId, D3DDEVTYPE_HAL, |
c357e426 |
248 | (HWND )myWindow->PlatformWindow()->NativeHandle(), |
62e1beed |
249 | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, |
d2eddacc |
250 | myD3dParams.get(), &myD3dDevice); |
62e1beed |
251 | if (isOK < 0) |
252 | { |
253 | return false; |
254 | } |
255 | |
256 | return myD3dDevice != NULL; |
257 | } |
258 | |
259 | // ======================================================================= |
260 | // function : d3dReset |
261 | // purpose : |
262 | // ======================================================================= |
c357e426 |
263 | bool D3DHost_View::d3dReset() |
62e1beed |
264 | { |
265 | if (myD3dDevice == NULL) |
266 | { |
267 | return false; |
268 | } |
269 | |
d2eddacc |
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; |
62e1beed |
275 | |
d2eddacc |
276 | HRESULT isOK = myD3dDevice->Reset(myD3dParams.get()); |
62e1beed |
277 | return isOK == D3D_OK; |
278 | } |
279 | |
280 | // ======================================================================= |
281 | // function : d3dCreateRenderTarget |
282 | // purpose : |
283 | // ======================================================================= |
c357e426 |
284 | bool D3DHost_View::d3dCreateRenderTarget() |
62e1beed |
285 | { |
6cde53c4 |
286 | bool toD3dFallback = false; |
62e1beed |
287 | if (myD3dWglFbo.IsNull()) |
288 | { |
289 | myD3dWglFbo = new D3DHost_FrameBuffer(); |
290 | } |
6cde53c4 |
291 | else |
62e1beed |
292 | { |
6cde53c4 |
293 | toD3dFallback = myD3dWglFbo->D3dFallback(); |
294 | } |
295 | |
296 | if (!toD3dFallback) |
297 | { |
298 | toD3dFallback = !myD3dWglFbo->InitD3dInterop (myWorkspace->GetGlContext(), |
299 | myD3dDevice, |
300 | myIsD3dEx, |
301 | myWindow->Width(), |
da2a6aee |
302 | myWindow->Height(), |
303 | 0); // do not request depth-stencil attachment since buffer will be flipped using addition FBO (myToFlipOutput) |
6cde53c4 |
304 | } |
305 | if (toD3dFallback) |
306 | { |
307 | if (!myD3dWglFbo->InitD3dFallback (myWorkspace->GetGlContext(), |
308 | myD3dDevice, |
309 | myIsD3dEx, |
310 | myWindow->Width(), |
da2a6aee |
311 | myWindow->Height(), |
312 | GL_DEPTH24_STENCIL8)) |
6cde53c4 |
313 | { |
314 | return false; |
315 | } |
62e1beed |
316 | } |
317 | |
318 | myD3dDevice->SetRenderTarget (0, myD3dWglFbo->D3dColorSurface()); |
319 | return true; |
320 | } |
321 | |
322 | // ======================================================================= |
323 | // function : d3dBeginRender |
324 | // purpose : |
325 | // ======================================================================= |
c357e426 |
326 | void D3DHost_View::d3dBeginRender() |
62e1beed |
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 | // ======================================================================= |
c357e426 |
342 | void D3DHost_View::d3dEndRender() |
62e1beed |
343 | { |
344 | if (myD3dDevice != NULL) |
345 | { |
346 | myD3dDevice->EndScene(); |
347 | } |
348 | } |
349 | |
350 | // ======================================================================= |
351 | // function : d3dSwap |
352 | // purpose : |
353 | // ======================================================================= |
c357e426 |
354 | bool D3DHost_View::d3dSwap() |
62e1beed |
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 | { |
6cde53c4 |
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)); |
62e1beed |
366 | } |
367 | return isOK == D3D_OK; |
368 | } |
369 | |
370 | // ======================================================================= |
371 | // function : Redraw |
372 | // purpose : |
373 | // ======================================================================= |
c357e426 |
374 | void D3DHost_View::Redraw() |
62e1beed |
375 | { |
c357e426 |
376 | if (!myWorkspace->Activate() |
62e1beed |
377 | || myD3dDevice == NULL) |
378 | { |
379 | return; |
380 | } |
b128c892 |
381 | else if (!myFBO.IsNull()) |
d2eddacc |
382 | { |
383 | OpenGl_View::Redraw(); |
384 | return; |
385 | } |
62e1beed |
386 | |
c357e426 |
387 | Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext(); |
6cde53c4 |
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 | |
c357e426 |
408 | myD3dWglFbo->LockSurface (aCtx); |
6cde53c4 |
409 | if (myD3dWglFbo->IsValid()) |
410 | { |
411 | myToFlipOutput = Standard_True; |
412 | myFBO = myD3dWglFbo; |
413 | } |
c357e426 |
414 | OpenGl_View::Redraw(); |
b128c892 |
415 | myFBO.Nullify(); |
c357e426 |
416 | myD3dWglFbo->UnlockSurface (aCtx); |
62e1beed |
417 | myToFlipOutput = Standard_False; |
c357e426 |
418 | if (aCtx->caps->buffersNoSwap) |
62e1beed |
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 | // ======================================================================= |
c357e426 |
439 | void D3DHost_View::RedrawImmediate() |
62e1beed |
440 | { |
c357e426 |
441 | Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext(); |
62e1beed |
442 | if (!myTransientDrawToFront |
443 | || !myBackBufferRestored |
c357e426 |
444 | || (aCtx->caps->buffersNoSwap && !myMainSceneFbos[0]->IsValid())) |
62e1beed |
445 | { |
c357e426 |
446 | Redraw(); |
62e1beed |
447 | return; |
448 | } |
c357e426 |
449 | else if (!myWorkspace->Activate() |
62e1beed |
450 | || myD3dDevice == NULL) |
451 | { |
452 | return; |
453 | } |
b128c892 |
454 | else if (!myFBO.IsNull()) |
d2eddacc |
455 | { |
456 | OpenGl_View::Redraw(); |
457 | return; |
458 | } |
62e1beed |
459 | |
c357e426 |
460 | myD3dWglFbo->LockSurface (aCtx); |
6cde53c4 |
461 | if (myD3dWglFbo->IsValid()) |
462 | { |
463 | myToFlipOutput = Standard_True; |
464 | myFBO = myD3dWglFbo; |
465 | } |
c357e426 |
466 | OpenGl_View::RedrawImmediate(); |
b128c892 |
467 | myFBO.Nullify(); |
c357e426 |
468 | myD3dWglFbo->UnlockSurface (aCtx); |
62e1beed |
469 | myToFlipOutput = Standard_False; |
c357e426 |
470 | if (aCtx->caps->buffersNoSwap) |
62e1beed |
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 | // ======================================================================= |
c357e426 |
491 | void D3DHost_View::Resized() |
62e1beed |
492 | { |
c357e426 |
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()) |
62e1beed |
498 | { |
499 | return; |
500 | } |
501 | |
502 | if (!myD3dWglFbo.IsNull()) |
503 | { |
c357e426 |
504 | myD3dWglFbo->Release (myWorkspace->GetGlContext().operator->()); |
62e1beed |
505 | } |
c357e426 |
506 | if (!myWorkspace->GetGlContext()->caps->buffersNoSwap) |
62e1beed |
507 | { |
c357e426 |
508 | d3dReset(); |
62e1beed |
509 | } |
510 | d3dCreateRenderTarget(); |
511 | } |