Diligent Graphics > Diligent Engine > Integration with Unity
Integration with Unity
Diligent Engine supports integration with Unity through Unity low-level native plugin interface. The engine relies on Native API Interoperability to attach to the graphics API initialized by Unity. After Diligent Engine device and context are created, they can be used us usual to create resources and issue rendering commands. Few API-specific steps need to be performed to ensure correct communication with Unity.
Direct3D11
Unity issues kUnityGfxDeviceEventInitialize event to let the plugin initialize its rendering resources. The following code snippet shows how Diligent Engine can be attached to D3D11 device returned by Unity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void RenderAPI_D3D11::ProcessDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces) { switch (type) { case kUnityGfxDeviceEventInitialize: { IUnityGraphicsD3D11* d3d = interfaces->Get<IUnityGraphicsD3D11>(); m_d3d11Device = d3d->GetDevice(); CComPtr<ID3D11DeviceContext> d3d11ImmediateContext; m_d3d11Device->GetImmediateContext(&d3d11ImmediateContext); auto *pFactoryD3d11 = GetEngineFactoryD3D11(); EngineD3D11Attribs Attribs; pFactoryD3d11->AttachToD3D11Device(m_d3d11Device, d3d11ImmediateContext, Attribs, &m_Device, &m_Context, 0); break; } case kUnityGfxDeviceEventShutdown: m_Context.Release(); m_Device.Release(); break; } } |
After the plugin completed with the rendering commands, it needs to call IRenderDevice::InvalidateState() to clear internal state cache.
1 2 3 4 5 |
void RenderAPI_D3D11::EndRendering() { if (m_Context) m_Context->InvalidateState(); } |
Direct3D12
To attach Diligent Engine to existing D3D12 device, it is necessary to implement ICommandQueueD3D12 interface (see Direct3D12 Interoperability for details). The following code shows how this interface can be implemented with the help of IUnityGraphicsD3D12v2 interface provided by Unity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
class UnityCommandQueueImpl : public ObjectBase<ICommandQueueD3D12> { public: using TBase = ObjectBase<ICommandQueueD3D12>; UnityCommandQueueImpl(IReferenceCounters *pRefCounters, IUnityGraphicsD3D12v2* pUnityGraphicsD3D12) : TBase(pRefCounters), m_pUnityGraphicsD3D12(pUnityGraphicsD3D12), m_WaitForGPUEventHandle( CreateEvent(nullptr, false, false, nullptr) ) {} ~UnityCommandQueueImpl() { CloseHandle(m_WaitForGPUEventHandle); } IMPLEMENT_QUERY_INTERFACE_IN_PLACE( IID_CommandQueueD3D12, TBase ) // Returns the fence value that will be signaled next time virtual UINT64 GetNextFenceValue()override final { return m_pUnityGraphicsD3D12->GetNextFrameFenceValue(); } // Executes a given command list virtual UINT64 ExecuteCommandList(ID3D12GraphicsCommandList* commandList)override final { auto NextFenceValue = m_pUnityGraphicsD3D12->GetNextFrameFenceValue(); m_CurrentFenceValue = m_pUnityGraphicsD3D12->ExecuteCommandList(commandList, static_cast<int>(m_ResourcesToTransition.size()), m_ResourcesToTransition.empty() ? nullptr : m_ResourcesToTransition.data()); m_ResourcesToTransition.clear(); return std::max(m_CurrentFenceValue, NextFenceValue); } // Returns D3D12 command queue. May return null if queue is anavailable virtual ID3D12CommandQueue* GetD3D12CommandQueue() { return nullptr; } // Returns value of the last completed fence virtual Uint64 GetCompletedFenceValue() { return m_pUnityGraphicsD3D12->GetFrameFence()->GetCompletedValue(); } // Blocks execution until all pending GPU commands are complete virtual void IdleGPU() { if (m_CurrentFenceValue < GetCompletedFenceValue()) { auto d3d12Fence = m_pUnityGraphicsD3D12->GetFrameFence(); d3d12Fence->SetEventOnCompletion(m_CurrentFenceValue, m_WaitForGPUEventHandle); <span id="j3a06e0c65">Therefore use the rectification properties embedded in <a href="https://unica-web.com/archive/2019/vincenzina-di-bartolo-jury-member-2019.html">levitra generic vs</a> this solution to be escaped and to manage the even degree of sugar level in blood. These semen-collecting pouches are seen only in various herbivorous animals, another meat-eating animal might also have these pouches. buy viagra cheap <a href="http://unica-web.com/archive/2016/english/presidents-letter-sept2016.html">unica-web.com</a> But the single 100mg pill has effects for 4-5 hours and a couple can have multiple orgasms. <a href="https://www.unica-web.com/watch/2018/not-the-end-of-the-world.html">overnight cialis tadalafil</a> pills are available with leading online pharmacies to help you win your game! Erectile dysfunction is by the use of certain pill and the pill ahs to taken only after a proper consultation from the doctor. Many times you might <a href="https://www.unica-web.com/archive/2011/General-Assembly/regulationscompetion11annex.pdf">levitra cheap</a> have heard about the common reactions to this drug. </span> WaitForSingleObject(m_WaitForGPUEventHandle, INFINITE); } } void TransitionResource(const UnityGraphicsD3D12ResourceState &ResourceState) { m_ResourcesToTransition.push_back(ResourceState); } private: IUnityGraphicsD3D12v2* const m_pUnityGraphicsD3D12; HANDLE m_WaitForGPUEventHandle = {}; UINT64 m_CurrentFenceValue = 0; std::vector<UnityGraphicsD3D12ResourceState> m_ResourcesToTransition; }; |
When Unity issues kUnityGfxDeviceEventInitialize event, IEngineFactoryD3D12::AttachToD3D12Device() method can be used to initialize Diligent Engine as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
void RenderAPI_D3D12::ProcessDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces) { switch (type) { case kUnityGfxDeviceEventInitialize: { m_UnityGraphicsD3D12 = interfaces->Get<IUnityGraphicsD3D12v2>(); auto d3d12Device = m_UnityGraphicsD3D12->GetDevice(); auto &DefaultAllocator = DefaultRawMemoryAllocator::GetAllocator(); m_CmdQueue = NEW_RC_OBJ(DefaultAllocator, "UnityCommandQueueImpl instance", UnityCommandQueueImpl) (m_UnityGraphicsD3D12); auto *pFactoryD3D12 = GetEngineFactoryD3D12(); EngineD3D12Attribs Attribs; pFactoryD3D12->AttachToD3D12Device(d3d12Device, m_CmdQueue, Attribs, &m_Device, &m_Context, 0); m_Device->QueryInterface(IID_RenderDeviceD3D12, reinterpret_cast<IObject**>(static_cast<IRenderDeviceD3D12**>(&m_RenderDeviceD3D12))); } break; case kUnityGfxDeviceEventShutdown: m_Context.Release(); m_Device.Release(); m_RenderDeviceD3D12.Release(); break; } } |
After the plugin completed with the rendering commands, it is necessary to explicitly flush the context to submit all commands for execution, invalidated context state and call IRenderDeviceD3D12::FinishFrame() to let the device perform necessary operations:
1 2 3 4 5 6 |
void RenderAPI_D3D12::EndRendering() { m_Context->Flush(); m_Context->InvalidateState(); m_RenderDeviceD3D12->FinishFrame(); } |
OpenGL/GLES
To attach Diligent Engine to an existing OpenGL context, call IEngineFactoryOpenGL::AttachToActiveGLContext():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void RenderAPI_OpenGLCoreES::ProcessDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces) { if (type == kUnityGfxDeviceEventInitialize) { auto *pFactoryGL = GetEngineFactoryOpenGL(); EngineCreationAttribs Attribs; pFactoryGL->AttachToActiveGLContext(Attribs, &m_Device, &m_Context); if (m_Context) { m_Context->QueryInterface(IID_DeviceContextGL, reinterpret_cast<IObject**>(static_cast<IDeviceContextGL**>(&m_DeviceCtxGL))); } } else if (type == kUnityGfxDeviceEventShutdown) { m_Context.Release(); m_Device.Release(); } } |
Unity uses multiple GL contexts. To update active GL context in Diligent Engine, it is necessary to call IDeviceContextGL::UpdateCurrentGLContext() before issuing any other command:
1 2 3 4 5 6 7 |
void RenderAPI_OpenGLCoreES::BeginRendering() { if(!m_DeviceCtxGL->UpdateCurrentGLContext()) return; RenderAPI::BeginRendering(); } |
Similar to other APIs, it is necessary to call IRenderDevice::InvalidateState() when all commands are issued to clear OpenGL state cache:
1 2 3 4 5 |
void RenderAPI_OpenGLCoreES::EndRendering() { if (m_Context) m_Context->InvalidateState(); } |