// Created on: 2015-06-10 // Created by: Kirill Gavrilov // Copyright (c) 2015 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. #include #include #include #include #include #include IMPLEMENT_STANDARD_RTTIEXT(D3DHost_View, OpenGl_View) namespace { enum D3DHost_VendorId { D3DHost_VendorId_AMD = 0x1002, D3DHost_VendorId_NVIDIA = 0x10DE, D3DHost_VendorId_Intel = 0x8086, }; } // namespace //================================================================================================= TCollection_AsciiString D3DHost_View::d3dFormatError(const long theErrCode) { switch (theErrCode) { case D3D_OK: return "OK"; case D3DERR_DEVICELOST: return "Device lost"; case D3DERR_DEVICEREMOVED: return "Device removed"; case D3DERR_DRIVERINTERNALERROR: return "Driver internal error"; case D3DERR_OUTOFVIDEOMEMORY: return "Out of video memory"; case D3DERR_INVALIDCALL: return "Invalid call"; default: return TCollection_AsciiString("Error #") + int(theErrCode) + ")"; } } //================================================================================================= D3DHost_View::D3DHost_View(const Handle(Graphic3d_StructureManager)& theMgr, const Handle(D3DHost_GraphicDriver)& theDriver, const Handle(OpenGl_Caps)& theCaps, OpenGl_StateCounter* theCounter) : OpenGl_View(theMgr, theDriver, theCaps, theCounter), myD3dLib(NULL), myD3dDevice(NULL), myD3dParams(new D3DPRESENT_PARAMETERS()), myRefreshRate(D3DPRESENT_RATE_DEFAULT), myIsD3dEx(false) { memset(myD3dParams.operator->(), 0, sizeof(D3DPRESENT_PARAMETERS)); myD3dParams->Windowed = TRUE; myD3dParams->SwapEffect = D3DSWAPEFFECT_DISCARD; myD3dParams->BackBufferFormat = D3DFMT_X8R8G8B8; myD3dParams->BackBufferCount = 1; myD3dParams->BackBufferHeight = 2; myD3dParams->BackBufferWidth = 2; myD3dParams->EnableAutoDepthStencil = FALSE; myD3dParams->AutoDepthStencilFormat = D3DFMT_D16_LOCKABLE; myD3dParams->FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; myD3dParams->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; } //================================================================================================= D3DHost_View::~D3DHost_View() { ReleaseGlResources(NULL); if (myD3dDevice != NULL) { myD3dDevice->Release(); myD3dDevice = NULL; } if (myD3dLib != NULL) { myD3dLib->Release(); myD3dLib = NULL; } } //================================================================================================= void D3DHost_View::ReleaseGlResources(const Handle(OpenGl_Context)& theCtx) { if (!myD3dWglFbo.IsNull()) { myD3dWglFbo->Release(theCtx.get()); myD3dWglFbo.Nullify(); } OpenGl_View::ReleaseGlResources(theCtx); } //================================================================================================= IDirect3DSurface9* D3DHost_View::D3dColorSurface() const { return myD3dWglFbo->D3dColorSurface(); } //================================================================================================= void D3DHost_View::SetWindow(const Handle(Graphic3d_CView)& theParentVIew, const Handle(Aspect_Window)& theWindow, const Aspect_RenderingContext theContext) { if (!myD3dWglFbo.IsNull()) { myD3dWglFbo->Release(myWorkspace->GetGlContext().operator->()); myD3dWglFbo.Nullify(); } if (myD3dDevice != NULL) { myD3dDevice->Release(); myD3dDevice = NULL; } OpenGl_View::SetWindow(theParentVIew, theWindow, theContext); if (!myWindow.IsNull()) { d3dInit(); d3dCreateRenderTarget(); } } //================================================================================================= void D3DHost_View::DiagnosticInformation(TColStd_IndexedDataMapOfStringString& theDict, Graphic3d_DiagnosticInfo theFlags) const { base_type::DiagnosticInformation(theDict, theFlags); if (myD3dDevice == NULL) { return; } D3DCAPS9 aDevCaps; memset(&aDevCaps, 0, sizeof(aDevCaps)); if (myD3dDevice->GetDeviceCaps(&aDevCaps) < 0) { return; } const UINT anAdapter = aDevCaps.AdapterOrdinal; D3DADAPTER_IDENTIFIER9 aDevId; memset(&aDevId, 0, sizeof(aDevId)); if (myD3dLib->GetAdapterIdentifier(anAdapter, 0, &aDevId) < 0) { return; } TCollection_AsciiString aVendorId((int)aDevId.VendorId); switch (aDevId.VendorId) { case D3DHost_VendorId_AMD: aVendorId = "AMD"; break; case D3DHost_VendorId_NVIDIA: aVendorId = "NVIDIA"; break; case D3DHost_VendorId_Intel: aVendorId = "Intel"; break; } theDict.Add("D3Dvendor", aVendorId); theDict.Add("D3Ddescription", aDevId.Description); theDict.Add("D3DdeviceName", aDevId.DeviceName); theDict.Add("D3Ddriver", aDevId.Driver); theDict.Add("D3DdeviceId", TCollection_AsciiString((int)aDevId.DeviceId)); theDict.Add("D3Dinterop", myD3dWglFbo.IsNull() || myD3dWglFbo->D3dFallback() ? "Software Fallback" : "WGL_NV_DX_interop"); } //================================================================================================= bool D3DHost_View::d3dInitLib() { if (myD3dLib == NULL) { IDirect3D9Ex* aD3dLibEx = NULL; // we link against d3d (using Direct3DCreate9 symbol), thus it should be already loaded HMODULE aLib = GetModuleHandleW(L"d3d9"); if (aLib != NULL) { // retrieve D3D9Ex function dynamically (available only since Vista+) typedef HRESULT(WINAPI * Direct3DCreate9Ex_t)(UINT, IDirect3D9Ex**); Direct3DCreate9Ex_t Direct3DCreate9ExProc = (Direct3DCreate9Ex_t)GetProcAddress(aLib, "Direct3DCreate9Ex"); if (Direct3DCreate9ExProc != NULL) { Direct3DCreate9ExProc(D3D_SDK_VERSION, &aD3dLibEx); } } myD3dLib = aD3dLibEx; myIsD3dEx = aD3dLibEx != NULL; if (myD3dLib == NULL) { myD3dLib = Direct3DCreate9(D3D_SDK_VERSION); } } return myD3dLib != NULL; } //================================================================================================= bool D3DHost_View::d3dInit() { if (!d3dInitLib()) { myWorkspace->GetGlContext()->PushMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, "Direct3DCreate9 failed!"); return false; } UINT anAdapterId = D3DADAPTER_DEFAULT; // setup the present parameters D3DDISPLAYMODE aCurrMode; memset(&aCurrMode, 0, sizeof(aCurrMode)); if (myD3dLib->GetAdapterDisplayMode(anAdapterId, &aCurrMode) == D3D_OK) { myD3dParams->BackBufferFormat = aCurrMode.Format; myRefreshRate = aCurrMode.RefreshRate; } myD3dParams->Windowed = TRUE; myD3dParams->BackBufferWidth = myWindow->Width(); myD3dParams->BackBufferHeight = myWindow->Height(); myD3dParams->hDeviceWindow = (HWND)myWindow->PlatformWindow()->NativeHandle(); // create the Video Device HRESULT isOK = myD3dLib->CreateDevice(anAdapterId, D3DDEVTYPE_HAL, (HWND)myWindow->PlatformWindow()->NativeHandle(), D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, myD3dParams.get(), &myD3dDevice); if (isOK < 0) { return false; } return myD3dDevice != NULL; } //================================================================================================= bool D3DHost_View::d3dReset() { if (myD3dDevice == NULL) { return false; } myD3dParams->Windowed = TRUE; myD3dParams->BackBufferWidth = myWindow->Width(); myD3dParams->BackBufferHeight = myWindow->Height(); myD3dParams->hDeviceWindow = (HWND)myWindow->PlatformWindow()->NativeHandle(); myD3dParams->FullScreen_RefreshRateInHz = !myD3dParams->Windowed ? myRefreshRate : 0; HRESULT isOK = myD3dDevice->Reset(myD3dParams.get()); return isOK == D3D_OK; } //================================================================================================= bool D3DHost_View::d3dCreateRenderTarget() { bool toD3dFallback = false; if (myD3dWglFbo.IsNull()) { myD3dWglFbo = new D3DHost_FrameBuffer(); } else { toD3dFallback = myD3dWglFbo->D3dFallback(); } if (!toD3dFallback) { toD3dFallback = !myD3dWglFbo->InitD3dInterop(myWorkspace->GetGlContext(), myD3dDevice, myIsD3dEx, myWindow->Width(), myWindow->Height(), // clang-format off 0); // do not request depth-stencil attachment since buffer will be flipped using addition FBO (myToFlipOutput) // clang-format on } if (toD3dFallback) { if (!myD3dWglFbo->InitD3dFallback(myWorkspace->GetGlContext(), myD3dDevice, myIsD3dEx, myWindow->Width(), myWindow->Height(), GL_DEPTH24_STENCIL8)) { return false; } } myD3dDevice->SetRenderTarget(0, myD3dWglFbo->D3dColorSurface()); return true; } //================================================================================================= void D3DHost_View::d3dBeginRender() { if (myD3dDevice == NULL) { return; } // clear the back buffer myD3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); myD3dDevice->BeginScene(); } //================================================================================================= void D3DHost_View::d3dEndRender() { if (myD3dDevice != NULL) { myD3dDevice->EndScene(); } } //================================================================================================= bool D3DHost_View::d3dSwap() { if (myD3dDevice == NULL) { return false; } const HRESULT isOK = myD3dDevice->Present(NULL, NULL, NULL, NULL); if (isOK != D3D_OK && isOK != S_PRESENT_OCCLUDED) { myWorkspace->GetGlContext()->PushMessage( GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, TCollection_AsciiString("Direct3D9, Present device failed, ") + d3dFormatError(isOK)); } return isOK == D3D_OK; } //================================================================================================= void D3DHost_View::Redraw() { if (!myWorkspace->Activate() || myD3dDevice == NULL) { return; } else if (!myFBO.IsNull()) { OpenGl_View::Redraw(); return; } Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext(); if (myWindow->PlatformWindow()->IsVirtual() && aCtx->arbFBO == NULL) { // do a dirty hack in extreme fallback mode with OpenGL driver not supporting FBO, // the back buffer of hidden window is used for rendering as offscreen buffer myTransientDrawToFront = false; int aWinSizeX = 0, aWinSizeY = 0; myWindow->PlatformWindow()->Size(aWinSizeX, aWinSizeY); WINDOWPLACEMENT aPlace; GetWindowPlacement((HWND)myWindow->PlatformWindow()->NativeHandle(), &aPlace); if (aPlace.rcNormalPosition.right - aPlace.rcNormalPosition.left != aWinSizeX || aPlace.rcNormalPosition.bottom - aPlace.rcNormalPosition.top != aWinSizeY) { aPlace.rcNormalPosition.right = aPlace.rcNormalPosition.left + aWinSizeX; aPlace.rcNormalPosition.bottom = aPlace.rcNormalPosition.top + aWinSizeY; aPlace.showCmd = SW_HIDE; SetWindowPlacement((HWND)myWindow->PlatformWindow()->NativeHandle(), &aPlace); } } myD3dWglFbo->LockSurface(aCtx); if (myD3dWglFbo->IsValid()) { myToFlipOutput = Standard_True; myFBO = myD3dWglFbo; } OpenGl_View::Redraw(); myFBO.Nullify(); myD3dWglFbo->UnlockSurface(aCtx); myToFlipOutput = Standard_False; if (aCtx->caps->buffersNoSwap) { return; } // blit result to the D3D back buffer and swap d3dBeginRender(); IDirect3DSurface9* aBackbuffer = NULL; myD3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &aBackbuffer); myD3dDevice->StretchRect(myD3dWglFbo->D3dColorSurface(), NULL, aBackbuffer, NULL, D3DTEXF_LINEAR); aBackbuffer->Release(); d3dEndRender(); d3dSwap(); } //================================================================================================= void D3DHost_View::RedrawImmediate() { Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext(); if (!myTransientDrawToFront || !myBackBufferRestored || (aCtx->caps->buffersNoSwap && !myMainSceneFbos[0]->IsValid())) { Redraw(); return; } else if (!myWorkspace->Activate() || myD3dDevice == NULL) { return; } else if (!myFBO.IsNull()) { OpenGl_View::Redraw(); return; } myD3dWglFbo->LockSurface(aCtx); if (myD3dWglFbo->IsValid()) { myToFlipOutput = Standard_True; myFBO = myD3dWglFbo; } OpenGl_View::RedrawImmediate(); myFBO.Nullify(); myD3dWglFbo->UnlockSurface(aCtx); myToFlipOutput = Standard_False; if (aCtx->caps->buffersNoSwap) { return; } // blit result to the D3D back buffer and swap d3dBeginRender(); IDirect3DSurface9* aBackbuffer = NULL; myD3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &aBackbuffer); myD3dDevice->StretchRect(myD3dWglFbo->D3dColorSurface(), NULL, aBackbuffer, NULL, D3DTEXF_LINEAR); aBackbuffer->Release(); d3dEndRender(); d3dSwap(); } //================================================================================================= void D3DHost_View::Resized() { const Standard_Integer aWidthOld = myWindow->Width(); const Standard_Integer aHeightOld = myWindow->Height(); OpenGl_View::Resized(); if (aWidthOld == myWindow->Width() && aHeightOld == myWindow->Height()) { return; } if (!myD3dWglFbo.IsNull()) { myD3dWglFbo->Release(myWorkspace->GetGlContext().operator->()); } if (!myWorkspace->GetGlContext()->caps->buffersNoSwap) { d3dReset(); } d3dCreateRenderTarget(); }