diff --git a/idd/LGIdd/CIndirectMonitorContext.cpp b/idd/LGIdd/CIndirectMonitorContext.cpp index 7f47a66a..efffca58 100644 --- a/idd/LGIdd/CIndirectMonitorContext.cpp +++ b/idd/LGIdd/CIndirectMonitorContext.cpp @@ -19,7 +19,6 @@ */ #include "CIndirectMonitorContext.h" -#include "CPlatformInfo.h" #include "CDebug.h" #include "CPipeServer.h" @@ -35,7 +34,8 @@ CIndirectMonitorContext::~CIndirectMonitorContext() m_devContext->OnMonitorDestroyed(m_monitor); } -void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent) +NTSTATUS CIndirectMonitorContext::AssignSwapChain( + IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent) { std::lock_guard assignGuard(m_assignMutex); @@ -48,43 +48,15 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re const UINT64 assignmentGeneration = m_assignmentGeneration.fetch_add(1, std::memory_order_acq_rel) + 1; - // Build the devices into locals so the members are never observed - // half-constructed and the expensive initialization stays outside m_lock. - std::shared_ptr dx11Device; - std::shared_ptr dx12Device; - - for (;;) + // Build the D3D11 device into a local so the member is never observed + // half-constructed. The worker binds it before performing the expensive + // D3D12, LGMP and post-processing initialization. + auto dx11Device = std::make_shared(renderAdapter); + const HRESULT initStatus = dx11Device->Init(); + if (FAILED(initStatus)) { - dx11Device = std::make_shared(renderAdapter); - if (FAILED(dx11Device->Init())) - { - WdfObjectDelete(swapChain); - return; - } - - UINT64 alignSize = CPlatformInfo::GetPageSize(); - dx12Device = std::make_shared(renderAdapter); - CD3D12Device::InitResult r = dx12Device->Init( - m_devContext->GetIVSHMEM(), alignSize, !dx11Device->IsSoftware()); - if (r == CD3D12Device::RETRY) - { - dx12Device.reset(); - dx11Device.reset(); - continue; - } - if (r == CD3D12Device::FAILURE) - { - WdfObjectDelete(swapChain); - return; - } - - if (!m_devContext->SetupLGMP(alignSize)) - { - WdfObjectDelete(swapChain); - DEBUG_ERROR("SetupLGMP failed"); - return; - } - break; + DEBUG_ERROR_HR(initStatus, "Failed to initialize D3D11 device"); + return STATUS_GRAPHICS_INDIRECT_DISPLAY_ABANDON_SWAPCHAIN; } AcquireSRWLockExclusive(&m_lock); @@ -92,7 +64,7 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re { ReleaseSRWLockExclusive(&m_lock); DEBUG_INFO("Swap chain assignment canceled before processor startup"); - return; + return STATUS_GRAPHICS_INDIRECT_DISPLAY_ABANDON_SWAPCHAIN; } // Publish the assignment atomically with starting its worker. An unassign @@ -100,11 +72,21 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re // signal and join the processor normally. m_devContext->OnSwapChainAssigned(); m_dx11Device = std::move(dx11Device); - m_dx12Device = std::move(dx12Device); m_swapChain.reset(new CSwapChainProcessor( this, assignmentGeneration, m_monitor, m_devContext, swapChain, - m_dx11Device, m_dx12Device, newFrameEvent)); + renderAdapter, m_dx11Device, newFrameEvent)); + if (!m_swapChain->Start()) + { + auto processor = std::move(m_swapChain); + dx11Device = std::move(m_dx11Device); + ReleaseSRWLockExclusive(&m_lock); + processor.reset(); + dx11Device.reset(); + m_devContext->OnSwapChainReleased(); + return STATUS_GRAPHICS_INDIRECT_DISPLAY_ABANDON_SWAPCHAIN; + } ReleaseSRWLockExclusive(&m_lock); + return STATUS_SUCCESS; } void CIndirectMonitorContext::DetachSwapChain() @@ -119,18 +101,15 @@ void CIndirectMonitorContext::DetachSwapChain() // method on another thread - holding the lock across that would deadlock. std::unique_ptr processor; std::shared_ptr dx11Device; - std::shared_ptr dx12Device; AcquireSRWLockExclusive(&m_lock); processor = std::move(m_swapChain); dx11Device = std::move(m_dx11Device); - dx12Device = std::move(m_dx12Device); ReleaseSRWLockExclusive(&m_lock); const bool hadSwapChain = !!processor; processor.reset(); dx11Device.reset(); - dx12Device.reset(); if (hadSwapChain) m_devContext->OnSwapChainReleased(); diff --git a/idd/LGIdd/CIndirectMonitorContext.h b/idd/LGIdd/CIndirectMonitorContext.h index fd42165c..30d58aeb 100644 --- a/idd/LGIdd/CIndirectMonitorContext.h +++ b/idd/LGIdd/CIndirectMonitorContext.h @@ -47,7 +47,6 @@ private: // while still allowing UnassignSwapChain to cancel the active one. std::mutex m_assignMutex; std::shared_ptr m_dx11Device; - std::shared_ptr m_dx12Device; CIndirectDeviceContext * m_devContext; std::unique_ptr m_swapChain; @@ -64,7 +63,8 @@ public: virtual ~CIndirectMonitorContext(); - void AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent); + NTSTATUS AssignSwapChain( + IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent); void UnassignSwapChain(); bool IsAssignmentCurrent(UINT64 generation) const { diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index 7eadb79e..76226a4d 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -20,8 +20,10 @@ #include "CSwapChainProcessor.h" #include "CIndirectMonitorContext.h" +#include "CPlatformInfo.h" #include +#include #include "CDebug.h" #include "CPipeServer.h" @@ -73,18 +75,96 @@ static bool FrameMetadataChanged(const D12FrameFormat& previous, CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContext, UINT64 assignmentGeneration, IDDCX_MONITOR monitor, - CIndirectDeviceContext* devContext, IDDCX_SWAPCHAIN hSwapChain, - std::shared_ptr dx11Device, std::shared_ptr dx12Device, HANDLE newFrameEvent) : + CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain, + LUID renderAdapter, std::shared_ptr dx11Device, + HANDLE newFrameEvent) : m_monitorContext(monitorContext), m_assignmentGeneration(assignmentGeneration), m_monitor(monitor), m_devContext(devContext), m_hSwapChain(hSwapChain), + m_renderAdapter(renderAdapter), m_dx11Device(dx11Device), - m_dx12Device(dx12Device), m_newFrameEvent(newFrameEvent) { - m_resPool.Init(dx11Device, dx12Device); + // Manual-reset: all worker threads wait on this, so it must stay signalled + // once set or only one thread would ever observe termination. + m_terminateEvent.Attach(CreateEvent(nullptr, TRUE, FALSE, nullptr)); + m_candidateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); + m_candidateAvailableEvent.Attach( + CreateEvent(nullptr, FALSE, FALSE, nullptr)); + m_publishTimer.Attach(CreateWaitableTimerExW(nullptr, nullptr, + CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS)); + if (!m_publishTimer.Get()) + m_publishTimer.Attach(CreateWaitableTimerExW( + nullptr, nullptr, 0, TIMER_ALL_ACCESS)); + m_cursorDataEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); + m_shapeBuffer = new (std::nothrow) BYTE[512 * 512 * 4]; +} + +bool CSwapChainProcessor::Start() +{ + if (!m_terminateEvent.Get() || !m_candidateEvent.Get() || + !m_candidateAvailableEvent.Get() || !m_publishTimer.Get() || + !m_cursorDataEvent.Get() || !m_shapeBuffer) + { + DEBUG_ERROR("Failed to initialize swap chain worker resources"); + return false; + } + + // Bind the swap chain before initializing the expensive transport pipeline. + m_thread[0].Attach(CreateThread( + nullptr, 0, _SwapChainThread, this, 0, nullptr)); + if (!m_thread[0].Get()) + { + DEBUG_ERROR_HR(GetLastError(), "Failed to create swap chain worker"); + return false; + } + return true; +} + +bool CSwapChainProcessor::InitializePipeline() +{ + for (;;) + { + if (!m_monitorContext->IsAssignmentCurrent(m_assignmentGeneration) || + WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0) + return false; + + UINT64 alignSize = CPlatformInfo::GetPageSize(); + auto dx12Device = std::make_shared(m_renderAdapter); + const CD3D12Device::InitResult result = dx12Device->Init( + m_devContext->GetIVSHMEM(), alignSize, !m_dx11Device->IsSoftware()); + if (result == CD3D12Device::RETRY) + { + const HRESULT deviceStatus = + m_dx11Device->GetDevice()->GetDeviceRemovedReason(); + if (FAILED(deviceStatus)) + { + DEBUG_ERROR_HR(deviceStatus, + "D3D11 device removed during D3D12 initialization"); + return false; + } + continue; + } + if (result == CD3D12Device::FAILURE) + return false; + + if (!m_devContext->SetupLGMP(alignSize)) + { + DEBUG_ERROR("SetupLGMP failed"); + return false; + } + + m_dx12Device = std::move(dx12Device); + break; + } + + if (!m_monitorContext->IsAssignmentCurrent(m_assignmentGeneration) || + WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0) + return false; + + m_resPool.Init(m_dx11Device, m_dx12Device); m_fbPool.Init(this); const bool enableEffects = !m_dx11Device->IsSoftware(); if (!enableEffects) @@ -92,7 +172,7 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex bool initialized = true; for (CPostProcessor& postProcessor : m_postProcessors) - if (!postProcessor.Init(dx12Device, enableEffects)) + if (!postProcessor.Init(m_dx12Device, enableEffects)) { initialized = false; break; @@ -112,32 +192,25 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex for (CPostProcessor& postProcessor : m_postProcessors) { postProcessor.Reset(); - if (!postProcessor.Init(dx12Device, false)) + if (!postProcessor.Init(m_dx12Device, false)) DEBUG_ERROR("Failed to initialize post processor copy support"); } DEBUG_WARN( "Failed to initialize post-processing effects; effects disabled"); } - // Manual-reset: all worker threads wait on this, so it must stay signalled - // once set or only one thread would ever observe termination. - m_terminateEvent.Attach(CreateEvent(nullptr, TRUE, FALSE, nullptr)); - m_candidateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); - m_candidateAvailableEvent.Attach( - CreateEvent(nullptr, FALSE, FALSE, nullptr)); - m_publishTimer.Attach(CreateWaitableTimerExW(nullptr, nullptr, - CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS)); - if (!m_publishTimer.Get()) - m_publishTimer.Attach(CreateWaitableTimerExW( - nullptr, nullptr, 0, TIMER_ALL_ACCESS)); - m_cursorDataEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); - m_shapeBuffer = new BYTE[512 * 512 * 4]; + if (!m_monitorContext->IsAssignmentCurrent(m_assignmentGeneration) || + WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0) + return false; - // Start the worker only after every object it can access is initialized. - m_thread[0].Attach(CreateThread( - nullptr, 0, _SwapChainThread, this, 0, nullptr)); m_thread[2].Attach(CreateThread( nullptr, 0, _PublisherThread, this, 0, nullptr)); + if (!m_thread[2].Get()) + { + DEBUG_ERROR_HR(GetLastError(), "Failed to create publisher thread"); + return false; + } + return true; } CSwapChainProcessor::~CSwapChainProcessor() @@ -153,8 +226,11 @@ CSwapChainProcessor::~CSwapChainProcessor() // Drain in-flight GPU work / completion callbacks before releasing the // resources they reference. The swap chain was already released in the // worker epilogue, so this does not hold an IddCx frame. - m_dx12Device->WaitForIdle(); - ResetCandidates(); + if (m_dx12Device) + { + m_dx12Device->WaitForIdle(); + ResetCandidates(); + } for (CPostProcessor& postProcessor : m_postProcessors) postProcessor.Reset(); @@ -353,27 +429,52 @@ void CSwapChainProcessor::SwapChainThread() DEBUG_INFO("Start Thread"); - // Only delete the swap chain if we took ownership of it (SetDevice - // succeeded). If SetDevice failed IddCx still owns and tears it down, so - // deleting it here would double-free the WDF object. Releasing it when we do - // own it hands the acquired frame back to IddCx promptly. - if (SwapChainThreadCore()) - WdfObjectDelete((WDFOBJECT)m_hSwapChain); + SwapChainThreadCore(); + + // Returning success from EvtIddCxMonitorAssignSwapChain transfers ownership + // to the driver, regardless of whether SetDevice or later initialization + // succeeds. Release it on every worker exit. + WdfObjectDelete((WDFOBJECT)m_hSwapChain); m_hSwapChain = nullptr; AvRevertMmThreadCharacteristics(avTaskHandle); } -bool CSwapChainProcessor::SwapChainThreadCore() +void CSwapChainProcessor::SwapChainThreadCore() { ComPtr dxgiDevice; HRESULT hr = m_dx11Device->GetDevice().As(&dxgiDevice); if (FAILED(hr)) { DEBUG_ERROR_HR(hr, "Failed to get the dxgiDevice"); - return false; + return; } + IDARG_IN_SWAPCHAINSETDEVICE setDevice = {}; + setDevice.pDevice = dxgiDevice.Get(); + + // IddCx can unassign a swap chain before its worker binds the device. Avoid + // using an invalidated handle; the worker epilogue still releases the + // driver-owned swap chain. + if (!m_monitorContext->IsAssignmentCurrent(m_assignmentGeneration) || + WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0) + return; + + // A failure here (commonly DXGI_ERROR_ACCESS_LOST on the first assignment) + // is not recoverable on this handle - IddCx reassigns a fresh swap chain, + // which is what actually succeeds. Bail cleanly and let that happen. + hr = IddCxSwapChainSetDevice(m_hSwapChain, &setDevice); + if (FAILED(hr)) + { + if (!m_monitorContext->IsAssignmentCurrent(m_assignmentGeneration) || + WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0) + DEBUG_INFO("Swap chain was unassigned during device setup"); + else + DEBUG_ERROR_HR(hr, "IddCxSwapChainSetDevice Failed"); + return; + } + DEBUG_INFO("Swap chain device set"); + if (IDD_IS_FUNCTION_AVAILABLE(IddCxSetRealtimeGPUPriority)) { DEBUG_INFO("Using IddCxSetRealtimeGPUPriority"); @@ -389,31 +490,12 @@ bool CSwapChainProcessor::SwapChainThreadCore() dxgiDevice->SetGPUThreadPriority(7); } - IDARG_IN_SWAPCHAINSETDEVICE setDevice = {}; - setDevice.pDevice = dxgiDevice.Get(); + if (!InitializePipeline()) + return; - // IddCx can unassign a swap chain while its devices are still being - // created. In that case the owner signals termination and IddCx retains - // responsibility for the handle because SetDevice has not succeeded. if (!m_monitorContext->IsAssignmentCurrent(m_assignmentGeneration) || WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0) - return false; - - // A failure here (commonly DXGI_ERROR_ACCESS_LOST on the first assignment) - // is not recoverable on this handle - IddCx reassigns a fresh swap chain, - // which is what actually succeeds. Bail cleanly and let that happen. - hr = IddCxSwapChainSetDevice(m_hSwapChain, &setDevice); - if (FAILED(hr)) - { - if (!m_monitorContext->IsAssignmentCurrent(m_assignmentGeneration) || - WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0) - DEBUG_INFO("Swap chain was unassigned during device setup"); - else - DEBUG_ERROR_HR(hr, "IddCxSwapChainSetDevice Failed"); - return false; - } - // Past this point SetDevice succeeded: we own the swap chain and are - // responsible for deleting it. + return; IDARG_IN_SETUP_HWCURSOR c = {}; c.CursorInfo.Size = sizeof(c.CursorInfo); @@ -426,7 +508,7 @@ bool CSwapChainProcessor::SwapChainThreadCore() if (!NT_SUCCESS(status)) { DEBUG_ERROR("IddCxMonitorSetupHardwareCursor Failed (0x%08x)", status); - return true; + return; } m_lastShapeId = 0; @@ -539,7 +621,6 @@ bool CSwapChainProcessor::SwapChainThreadCore() break; } - return true; } void CSwapChainProcessor::CandidateCompletionFunction( diff --git a/idd/LGIdd/CSwapChainProcessor.h b/idd/LGIdd/CSwapChainProcessor.h index 45a44600..4aae4445 100644 --- a/idd/LGIdd/CSwapChainProcessor.h +++ b/idd/LGIdd/CSwapChainProcessor.h @@ -47,6 +47,7 @@ private: IDDCX_MONITOR m_monitor; CIndirectDeviceContext * m_devContext; IDDCX_SWAPCHAIN m_hSwapChain; + LUID m_renderAdapter; std::shared_ptr m_dx11Device; std::shared_ptr m_dx12Device; HANDLE m_newFrameEvent; @@ -139,7 +140,8 @@ private: static DWORD CALLBACK _SwapChainThread(LPVOID arg); void SwapChainThread(); - bool SwapChainThreadCore(); + void SwapChainThreadCore(); + bool InitializePipeline(); static DWORD CALLBACK _PublisherThread(LPVOID arg); void PublisherThread(); @@ -176,8 +178,10 @@ private: public: CSwapChainProcessor(CIndirectMonitorContext * monitorContext, UINT64 assignmentGeneration, IDDCX_MONITOR monitor, CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain, - std::shared_ptr dx11Device, std::shared_ptr dx12Device, HANDLE newFrameEvent); + LUID renderAdapter, std::shared_ptr dx11Device, + HANDLE newFrameEvent); ~CSwapChainProcessor(); + bool Start(); CIndirectDeviceContext * GetDevice() { return m_devContext; } std::shared_ptr GetD3D12Device() { return m_dx12Device; } diff --git a/idd/LGIdd/Device.cpp b/idd/LGIdd/Device.cpp index e8f44c7b..0a0da20b 100644 --- a/idd/LGIdd/Device.cpp +++ b/idd/LGIdd/Device.cpp @@ -256,9 +256,8 @@ NTSTATUS LGIddMonitorAssignSwapChain(IDDCX_MONITOR monitor, const IDARG_IN_SETSW { DEBUG_INFO("Swap chain assigned to monitor %p", monitor); auto * wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor); - wrapper->context->AssignSwapChain( + return wrapper->context->AssignSwapChain( inArgs->hSwapChain, inArgs->RenderAdapterLuid, inArgs->hNextSurfaceAvailable); - return STATUS_SUCCESS; } NTSTATUS LGIddMonitorUnassignSwapChain(IDDCX_MONITOR monitor)