mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-17 04:42:01 +00:00
[idd] stop using IddCxMonitorUpdateModes(2)
While it would make much more sense to use these then a full replug to change modes, Microsoft have not properly implemented the API to clear the cached monitor mode states internally, making these calls useless. Revert to just replugging the device on mode change
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include <wdf.h>
|
||||
#include <wrl.h>
|
||||
#include <d3d12.h>
|
||||
#include <atomic>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
@@ -40,7 +41,7 @@ class CD3D12CommandQueue
|
||||
ComPtr<ID3D12CommandList > m_cmdList;
|
||||
ComPtr<ID3D12Fence > m_fence;
|
||||
|
||||
bool m_pending = false;
|
||||
std::atomic<bool> m_pending = false;
|
||||
HandleT<HANDLENullTraits> m_event;
|
||||
HANDLE m_waitHandle = INVALID_HANDLE_VALUE;
|
||||
UINT64 m_fenceValue = 0;
|
||||
|
||||
@@ -147,6 +147,22 @@ void CD3D12Device::DeInit()
|
||||
m_infoQueue.Reset();
|
||||
}
|
||||
|
||||
void CD3D12Device::WaitForIdle()
|
||||
{
|
||||
// A queue is ready once its GPU work has signalled and its completion
|
||||
// callback has run (clearing the pending flag). Bound the wait so a
|
||||
// removed/hung device cannot stall teardown indefinitely.
|
||||
auto drain = [](CD3D12CommandQueue& queue)
|
||||
{
|
||||
for (int i = 0; i < 1000 && !queue.IsReady(); ++i)
|
||||
Sleep(1);
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(m_copyQueue); ++i)
|
||||
drain(m_copyQueue[i]);
|
||||
drain(m_computeQueue);
|
||||
}
|
||||
|
||||
bool CD3D12Device::HeapTest()
|
||||
{
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
|
||||
@@ -69,6 +69,11 @@ struct CD3D12Device
|
||||
InitResult Init(CIVSHMEM &ivshmem, UINT64 &alignSize);
|
||||
void DeInit();
|
||||
|
||||
// Wait for all command queues to finish in-flight GPU work and run their
|
||||
// completion callbacks. Used at swap-chain teardown so no callback touches
|
||||
// resources we are about to release.
|
||||
void WaitForIdle();
|
||||
|
||||
ComPtr<ID3D12Device3> GetDevice() { return m_device; }
|
||||
ComPtr<ID3D12Heap > GetHeap() { return m_ivshmemHeap; }
|
||||
bool IsIndirectCopy() { return m_indirectCopy; }
|
||||
|
||||
@@ -278,6 +278,17 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
|
||||
void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
|
||||
{
|
||||
// We support a single monitor; never create a second one if one already
|
||||
// exists (a replug must clear m_monitor via departure first).
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
bool haveMonitor = m_monitor != WDF_NO_HANDLE;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
if (haveMonitor)
|
||||
{
|
||||
DEBUG_WARN("FinishInit skipped: a monitor already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES attr;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, CIndirectMonitorContextWrapper);
|
||||
|
||||
@@ -332,18 +343,7 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
|
||||
|
||||
void CIndirectDeviceContext::ReplugMonitor()
|
||||
{
|
||||
// Sample and mutate the replug state atomically. The monitor handle is
|
||||
// published by FinishInit on an IddCx thread while this can run from the
|
||||
// timer thread, so a plain check-then-act would race.
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
IDDCX_MONITOR monitor = m_monitor;
|
||||
|
||||
if (monitor == WDF_NO_HANDLE)
|
||||
{
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
FinishInit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_replugMonitor)
|
||||
{
|
||||
@@ -351,7 +351,21 @@ void CIndirectDeviceContext::ReplugMonitor()
|
||||
return;
|
||||
}
|
||||
|
||||
IDDCX_MONITOR monitor = m_monitor;
|
||||
if (monitor == WDF_NO_HANDLE)
|
||||
{
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
// Either no monitor yet, or one is already pending; build it now and
|
||||
// cancel any queued rebuild so we do not create two.
|
||||
InterlockedExchange(&m_finishInitQueued, 0);
|
||||
FinishInit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the handle before departing so nothing calls an IddCx monitor API on
|
||||
// a departing/destroyed handle. FinishInit publishes the new one.
|
||||
m_replugMonitor = true;
|
||||
m_monitor = nullptr;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
|
||||
DEBUG_TRACE("ReplugMonitor");
|
||||
@@ -360,16 +374,28 @@ void CIndirectDeviceContext::ReplugMonitor()
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
m_replugMonitor = false;
|
||||
m_monitor = monitor;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
DEBUG_ERROR("IddCxMonitorDeparture Failed (0x%08x)", status);
|
||||
return;
|
||||
}
|
||||
|
||||
// Always queue the rebuild here rather than relying on the unassign callback:
|
||||
// if the monitor had no swap chain (e.g. recovering from a lost path) no
|
||||
// unassign fires. The timer runs FinishInit off this thread.
|
||||
InterlockedExchange(&m_finishInitQueued, 1);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::OnMonitorDestroyed(IDDCX_MONITOR monitor)
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
if (m_monitor == monitor)
|
||||
m_monitor = nullptr;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::OnAssignSwapChain()
|
||||
{
|
||||
InterlockedExchange(&m_recoverModeUpdateSwapChain, 0);
|
||||
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
bool doSetMode = m_doSetMode;
|
||||
CSettings::DisplayMode mode = m_setMode;
|
||||
@@ -380,40 +406,6 @@ void CIndirectDeviceContext::OnAssignSwapChain()
|
||||
g_pipe.SetDisplayMode(mode.width, mode.height, mode.refresh);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::OnUnassignedSwapChain()
|
||||
{
|
||||
InterlockedExchange(&m_replugMonitorQueued, 0);
|
||||
InterlockedExchange(&m_recoverModeUpdateSwapChain, 0);
|
||||
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
bool replug = m_replugMonitor;
|
||||
m_replugMonitor = false;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
|
||||
// Do NOT rebuild the monitor from inside this callback. Creating and arriving
|
||||
// a new monitor here re-enters IddCx while the old monitor's swap-chain
|
||||
// teardown is still unwinding on this thread, which leaves the new swap
|
||||
// chain's surfaces lost/abandoned (DXGI_ERROR_ACCESS_LOST). Defer to the LGMP
|
||||
// timer, matching how the departure is scheduled.
|
||||
if (replug)
|
||||
InterlockedExchange(&m_finishInitQueued, 1);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::OnSwapChainLost()
|
||||
{
|
||||
// A mode update normally keeps the swap chain alive. If Windows instead
|
||||
// reports the existing path disappeared before we see a frame at the new
|
||||
// size, recover by scheduling the old replug path from the LGMP timer so we
|
||||
// do not tear down the swap chain from one of its worker threads.
|
||||
if (!InterlockedCompareExchange(&m_recoverModeUpdateSwapChain, 0, 0))
|
||||
return;
|
||||
|
||||
if (InterlockedExchange(&m_replugMonitorQueued, 1))
|
||||
return;
|
||||
|
||||
DEBUG_WARN("Swap chain was lost after a mode update, falling back to monitor replug");
|
||||
}
|
||||
|
||||
static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO & mode, DWORD width, DWORD height, DWORD vsync, bool monitorMode)
|
||||
{
|
||||
mode.totalSize.cx = mode.activeSize.cx = width;
|
||||
@@ -574,96 +566,6 @@ NTSTATUS CIndirectDeviceContext::MonitorQueryTargetModes2(
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CIndirectDeviceContext::UpdateMonitorModes()
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
IDDCX_MONITOR monitor = m_monitor;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
|
||||
if (!monitor)
|
||||
return false;
|
||||
|
||||
// Snapshot the mode list so we do not hold m_modeLock across the IddCx call
|
||||
// below - IddCxMonitorUpdateModes can synchronously re-enter our mode
|
||||
// enumeration callbacks (which also take m_modeLock).
|
||||
CSettings::DisplayModes displayModes;
|
||||
AcquireSRWLockShared(&m_modeLock);
|
||||
displayModes = m_displayModes;
|
||||
ReleaseSRWLockShared(&m_modeLock);
|
||||
|
||||
#ifdef HAS_IDDCX_110
|
||||
if (CanUseIddCx110DDIs())
|
||||
{
|
||||
IDDCX_TARGET_MODE2* modes = (IDDCX_TARGET_MODE2*)_malloca(
|
||||
displayModes.size() * sizeof(IDDCX_TARGET_MODE2));
|
||||
if (!modes)
|
||||
{
|
||||
DEBUG_WARN("Failed to allocate memory for the mode list");
|
||||
return false;
|
||||
}
|
||||
|
||||
ZeroMemory(modes, displayModes.size() * sizeof(IDDCX_TARGET_MODE2));
|
||||
|
||||
auto* mode = modes;
|
||||
for (auto it = displayModes.cbegin(); it != displayModes.cend(); ++it, ++mode)
|
||||
{
|
||||
mode->Size = sizeof(IDDCX_TARGET_MODE2);
|
||||
mode->RequiredBandwidth = (UINT64)it->width * it->height * it->refresh * 32;
|
||||
mode->BitsPerComponent = GetWireBitsPerComponent(CanUseIddCx110DDIs());
|
||||
FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, false);
|
||||
}
|
||||
|
||||
IDARG_IN_UPDATEMODES2 updateModes = {};
|
||||
updateModes.Reason = IDDCX_UPDATE_REASON_OTHER;
|
||||
updateModes.TargetModeCount = (UINT)displayModes.size();
|
||||
updateModes.pTargetModes = modes;
|
||||
|
||||
NTSTATUS status = IddCxMonitorUpdateModes2(monitor, &updateModes);
|
||||
_freea(modes);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DEBUG_WARN("IddCxMonitorUpdateModes2 Failed (0x%08x)", status);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
IDDCX_TARGET_MODE* modes = (IDDCX_TARGET_MODE*)_malloca(
|
||||
displayModes.size() * sizeof(IDDCX_TARGET_MODE));
|
||||
if (!modes)
|
||||
{
|
||||
DEBUG_WARN("Failed to allocate memory for the mode list");
|
||||
return false;
|
||||
}
|
||||
|
||||
ZeroMemory(modes, displayModes.size() * sizeof(IDDCX_TARGET_MODE));
|
||||
|
||||
auto* mode = modes;
|
||||
for (auto it = displayModes.cbegin(); it != displayModes.cend(); ++it, ++mode)
|
||||
{
|
||||
mode->Size = sizeof(IDDCX_TARGET_MODE);
|
||||
mode->RequiredBandwidth = (UINT64)it->width * it->height * it->refresh * 32;
|
||||
FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, false);
|
||||
}
|
||||
|
||||
IDARG_IN_UPDATEMODES updateModes = {};
|
||||
updateModes.Reason = IDDCX_UPDATE_REASON_OTHER;
|
||||
updateModes.TargetModeCount = (UINT)displayModes.size();
|
||||
updateModes.pTargetModes = modes;
|
||||
|
||||
NTSTATUS status = IddCxMonitorUpdateModes(monitor, &updateModes);
|
||||
_freea(modes);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DEBUG_WARN("IddCxMonitorUpdateModes Failed (0x%08x)", status);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::SetResolution(int width, int height)
|
||||
{
|
||||
CSettings::DisplayMode mode = {};
|
||||
@@ -674,28 +576,16 @@ void CIndirectDeviceContext::SetResolution(int width, int height)
|
||||
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
m_setMode = mode;
|
||||
m_doSetMode = true;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
|
||||
g_settings.SetExtraMode(mode);
|
||||
|
||||
PopulateDefaultModes();
|
||||
|
||||
if (UpdateMonitorModes())
|
||||
{
|
||||
DEBUG_TRACE("Updated monitor modes without replugging");
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
m_doSetMode = false;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
InterlockedExchange(&m_recoverModeUpdateSwapChain, 1);
|
||||
g_pipe.SetDisplayMode(mode.width, mode.height, mode.refresh);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Falling back to monitor replug for mode update");
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
m_doSetMode = true;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
InterlockedExchange(&m_recoverModeUpdateSwapChain, 0);
|
||||
// IddCxMonitorUpdateModes[2] does not invalidate Windows' cached mode list,
|
||||
// so the only reliable way to apply a new mode is to depart and re-arrive the
|
||||
// monitor, forcing Windows to rebuild the topology from the new mode list.
|
||||
ReplugMonitor();
|
||||
}
|
||||
|
||||
@@ -909,20 +799,13 @@ void CIndirectDeviceContext::DeInitLGMP()
|
||||
|
||||
void CIndirectDeviceContext::LGMPTimer()
|
||||
{
|
||||
if (InterlockedExchange(&m_replugMonitorQueued, 0))
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
m_doSetMode = true;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
ReplugMonitor();
|
||||
return;
|
||||
}
|
||||
|
||||
// Rebuild the monitor deferred from the IddCx unassign callback, off that
|
||||
// callback's thread and after its swap-chain teardown has fully unwound.
|
||||
// Rebuild the monitor queued by ReplugMonitor, off the IddCx callback thread.
|
||||
if (InterlockedExchange(&m_finishInitQueued, 0))
|
||||
{
|
||||
FinishInit(0);
|
||||
AcquireSRWLockExclusive(&m_stateLock);
|
||||
m_replugMonitor = false;
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -984,17 +867,6 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
if (!m_lgmp || !m_frameQueue)
|
||||
return result;
|
||||
|
||||
if (InterlockedCompareExchange(&m_recoverModeUpdateSwapChain, 0, 0))
|
||||
{
|
||||
AcquireSRWLockShared(&m_stateLock);
|
||||
const bool sizeMatches =
|
||||
srcFormat.width == m_setMode.width && srcFormat.height == m_setMode.height;
|
||||
ReleaseSRWLockShared(&m_stateLock);
|
||||
|
||||
if (sizeMatches)
|
||||
InterlockedExchange(&m_recoverModeUpdateSwapChain, 0);
|
||||
}
|
||||
|
||||
if (m_width != dstFormat.desc.Width ||
|
||||
m_height != dstFormat.desc.Height ||
|
||||
m_pitch != pitch ||
|
||||
|
||||
@@ -140,7 +140,6 @@ private:
|
||||
void DeInitLGMP();
|
||||
void LGMPTimer();
|
||||
void ResendCursor() const;
|
||||
bool UpdateMonitorModes();
|
||||
|
||||
// Guards m_displayModes and m_edid. The mode list is rebuilt on the LGMP
|
||||
// timer thread (SetResolution) while IddCx concurrently enumerates it on its
|
||||
@@ -153,13 +152,9 @@ private:
|
||||
|
||||
CSettings::DisplayMode m_setMode = {};
|
||||
bool m_doSetMode = false;
|
||||
volatile LONG m_replugMonitorQueued = 0;
|
||||
volatile LONG m_recoverModeUpdateSwapChain = 0;
|
||||
// Set from the IddCx unassign callback to defer the monitor rebuild
|
||||
// (FinishInit) onto the LGMP timer. Creating/arriving a new monitor from
|
||||
// inside the old monitor's unassign callback re-enters IddCx while its
|
||||
// swap-chain teardown is still unwinding, which leaves the new swap-chain
|
||||
// surfaces in a lost/abandoned state.
|
||||
|
||||
// Set by ReplugMonitor after a departure to rebuild the monitor from the LGMP
|
||||
// timer, off the IddCx callback thread.
|
||||
volatile LONG m_finishInitQueued = 0;
|
||||
|
||||
public:
|
||||
@@ -175,9 +170,8 @@ public:
|
||||
void FinishInit(UINT connectorIndex);
|
||||
void ReplugMonitor();
|
||||
|
||||
void OnMonitorDestroyed(IDDCX_MONITOR monitor);
|
||||
void OnAssignSwapChain();
|
||||
void OnUnassignedSwapChain();
|
||||
void OnSwapChainLost();
|
||||
|
||||
NTSTATUS ParseMonitorDescription(
|
||||
const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs);
|
||||
|
||||
@@ -32,35 +32,41 @@ CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIn
|
||||
CIndirectMonitorContext::~CIndirectMonitorContext()
|
||||
{
|
||||
UnassignSwapChain();
|
||||
m_devContext->OnMonitorDestroyed(m_monitor);
|
||||
}
|
||||
|
||||
void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent)
|
||||
{
|
||||
reInit:
|
||||
UnassignSwapChain();
|
||||
|
||||
m_dx11Device = std::make_shared<CD3D11Device>(renderAdapter);
|
||||
if (FAILED(m_dx11Device->Init()))
|
||||
// Build everything into locals so the members are never observed
|
||||
// half-constructed by a concurrent unassign, and so the processor (which
|
||||
// spawns a worker thread) is created outside the lock.
|
||||
std::shared_ptr<CD3D11Device> dx11Device;
|
||||
std::shared_ptr<CD3D12Device> dx12Device;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
dx11Device = std::make_shared<CD3D11Device>(renderAdapter);
|
||||
if (FAILED(dx11Device->Init()))
|
||||
{
|
||||
WdfObjectDelete(swapChain);
|
||||
return;
|
||||
}
|
||||
|
||||
UINT64 alignSize = CPlatformInfo::GetPageSize();
|
||||
m_dx12Device = std::make_shared<CD3D12Device>(renderAdapter);
|
||||
switch (m_dx12Device->Init(m_devContext->GetIVSHMEM(), alignSize))
|
||||
dx12Device = std::make_shared<CD3D12Device>(renderAdapter);
|
||||
CD3D12Device::InitResult r = dx12Device->Init(m_devContext->GetIVSHMEM(), alignSize);
|
||||
if (r == CD3D12Device::RETRY)
|
||||
{
|
||||
dx12Device.reset();
|
||||
dx11Device.reset();
|
||||
continue;
|
||||
}
|
||||
if (r == CD3D12Device::FAILURE)
|
||||
{
|
||||
case CD3D12Device::SUCCESS:
|
||||
break;
|
||||
|
||||
case CD3D12Device::FAILURE:
|
||||
WdfObjectDelete(swapChain);
|
||||
return;
|
||||
|
||||
case CD3D12Device::RETRY:
|
||||
m_dx12Device.reset();
|
||||
m_dx11Device.reset();
|
||||
goto reInit;
|
||||
}
|
||||
|
||||
if (!m_devContext->SetupLGMP(alignSize))
|
||||
@@ -69,13 +75,35 @@ reInit:
|
||||
DEBUG_ERROR("SetupLGMP failed");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
m_swapChain.reset(new CSwapChainProcessor(m_monitor, m_devContext, swapChain, m_dx11Device, m_dx12Device, newFrameEvent));
|
||||
std::unique_ptr<CSwapChainProcessor> processor(new CSwapChainProcessor(
|
||||
m_monitor, m_devContext, swapChain, dx11Device, dx12Device, newFrameEvent));
|
||||
|
||||
AcquireSRWLockExclusive(&m_lock);
|
||||
m_dx11Device = std::move(dx11Device);
|
||||
m_dx12Device = std::move(dx12Device);
|
||||
m_swapChain = std::move(processor);
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
}
|
||||
|
||||
void CIndirectMonitorContext::UnassignSwapChain()
|
||||
{
|
||||
m_swapChain.reset();
|
||||
m_dx11Device.reset();
|
||||
m_dx12Device.reset();
|
||||
// Detach under the lock, then destroy outside it. Destroying the processor
|
||||
// joins its worker thread, whose teardown (WdfObjectDelete) re-enters this
|
||||
// method on another thread - holding the lock across that would deadlock.
|
||||
std::unique_ptr<CSwapChainProcessor> processor;
|
||||
std::shared_ptr<CD3D11Device> dx11Device;
|
||||
std::shared_ptr<CD3D12Device> dx12Device;
|
||||
|
||||
AcquireSRWLockExclusive(&m_lock);
|
||||
processor = std::move(m_swapChain);
|
||||
dx11Device = std::move(m_dx11Device);
|
||||
dx12Device = std::move(m_dx12Device);
|
||||
ReleaseSRWLockExclusive(&m_lock);
|
||||
|
||||
processor.reset();
|
||||
dx11Device.reset();
|
||||
dx12Device.reset();
|
||||
}
|
||||
@@ -35,6 +35,10 @@ class CIndirectMonitorContext
|
||||
private:
|
||||
IDDCX_MONITOR m_monitor;
|
||||
|
||||
// Guards the swap chain and device pointers. Assign and unassign can run
|
||||
// concurrently (an unassign triggered by the worker's WdfObjectDelete can
|
||||
// race the next assign), and shared_ptr copy/reset is not thread safe.
|
||||
SRWLOCK m_lock = SRWLOCK_INIT;
|
||||
std::shared_ptr<CD3D11Device> m_dx11Device;
|
||||
std::shared_ptr<CD3D12Device> m_dx12Device;
|
||||
|
||||
|
||||
@@ -38,7 +38,9 @@ CSwapChainProcessor::CSwapChainProcessor(IDDCX_MONITOR monitor, CIndirectDeviceC
|
||||
if (!m_postProcessor.Init(dx12Device))
|
||||
DEBUG_ERROR("Failed to initialize post processor");
|
||||
|
||||
m_terminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||
// Manual-reset: both 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_thread[0].Attach(CreateThread(nullptr, 0, _SwapChainThread, this, 0, nullptr));
|
||||
|
||||
m_cursorDataEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||
@@ -53,6 +55,11 @@ CSwapChainProcessor::~CSwapChainProcessor()
|
||||
if (m_thread[1].Get())
|
||||
WaitForSingleObject(m_thread[1].Get(), INFINITE);
|
||||
|
||||
// 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();
|
||||
|
||||
m_postProcessor.Reset();
|
||||
m_resPool.Reset();
|
||||
m_fbPool.Reset();
|
||||
@@ -71,22 +78,26 @@ void CSwapChainProcessor::SwapChainThread()
|
||||
HANDLE avTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &avTask);
|
||||
|
||||
DEBUG_INFO("Start Thread");
|
||||
SwapChainThreadCore();
|
||||
|
||||
// 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);
|
||||
m_hSwapChain = nullptr;
|
||||
|
||||
AvRevertMmThreadCharacteristics(avTaskHandle);
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::SwapChainThreadCore()
|
||||
bool CSwapChainProcessor::SwapChainThreadCore()
|
||||
{
|
||||
ComPtr<IDXGIDevice> dxgiDevice;
|
||||
HRESULT hr = m_dx11Device->GetDevice().As(&dxgiDevice);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to get the dxgiDevice");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IDD_IS_FUNCTION_AVAILABLE(IddCxSetRealtimeGPUPriority))
|
||||
@@ -107,12 +118,17 @@ void CSwapChainProcessor::SwapChainThreadCore()
|
||||
IDARG_IN_SWAPCHAINSETDEVICE setDevice = {};
|
||||
setDevice.pDevice = dxgiDevice.Get();
|
||||
|
||||
// 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))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "IddCxSwapChainSetDevice Failed");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// Past this point SetDevice succeeded: we own the swap chain and are
|
||||
// responsible for deleting it.
|
||||
|
||||
IDARG_IN_SETUP_HWCURSOR c = {};
|
||||
c.CursorInfo.Size = sizeof(c.CursorInfo);
|
||||
@@ -125,7 +141,7 @@ void CSwapChainProcessor::SwapChainThreadCore()
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DEBUG_ERROR("IddCxMonitorSetupHardwareCursor Failed (0x%08x)", status);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
m_lastShapeId = 0;
|
||||
@@ -206,9 +222,9 @@ void CSwapChainProcessor::SwapChainThreadCore()
|
||||
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
if (hr == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
|
||||
m_devContext->OnSwapChainLost();
|
||||
else
|
||||
// A lost path is normal (mode change/topology rebuild); Windows
|
||||
// reassigns a fresh swap chain. Just exit and let it.
|
||||
if (hr != STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
|
||||
DEBUG_ERROR_HR(hr, "IddCxSwapChainFinishedProcessingFrame Failed");
|
||||
break;
|
||||
}
|
||||
@@ -216,12 +232,10 @@ void CSwapChainProcessor::SwapChainThreadCore()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hr == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
|
||||
m_devContext->OnSwapChainLost();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::CompletionFunction(
|
||||
@@ -584,7 +598,6 @@ bool CSwapChainProcessor::QueryHWCursor()
|
||||
// this occurs if the display went away (ie, screen blanking or disabled)
|
||||
if (status == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
|
||||
{
|
||||
m_devContext->OnSwapChainLost();
|
||||
SetEvent(m_terminateEvent.Get());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ private:
|
||||
|
||||
static DWORD CALLBACK _SwapChainThread(LPVOID arg);
|
||||
void SwapChainThread();
|
||||
void SwapChainThreadCore();
|
||||
bool SwapChainThreadCore();
|
||||
|
||||
static DWORD CALLBACK _CursorThread(LPVOID arg);
|
||||
bool QueryHWCursor();
|
||||
|
||||
@@ -199,7 +199,6 @@ NTSTATUS LGIddMonitorUnassignSwapChain(IDDCX_MONITOR monitor)
|
||||
{
|
||||
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
|
||||
wrapper->context->UnassignSwapChain();
|
||||
wrapper->context->GetDeviceContext()->OnUnassignedSwapChain();
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user