[idd] keep the generated EDID immutable

Generate the EDID once during adapter initialization and update only the
live mode list when resolutions change.

Changing the EDID during a monitor replug causes Windows to identify it
as a different monitor, losing the existing display configuration.
Reusing the original EDID preserves the monitor identity while still
allowing dynamic modes to be advertised through IddCx.
This commit is contained in:
Geoffrey McRae
2026-07-18 02:14:12 +10:00
parent b69dd4d897
commit 37d7f0e48f
2 changed files with 18 additions and 14 deletions

View File

@@ -99,21 +99,25 @@ void CIndirectDeviceContext::PopulateDefaultModes()
{ {
g_settings.LoadModes(); g_settings.LoadModes();
// Build the new mode list and EDID into locals first so we only hold the // Build the new mode list into a local first so we only hold the lock for
// lock for the swap. IddCx readers may be iterating the live containers on // the swap. IddCx readers may be iterating the live container on another
// another thread; a clear()/push_back() under them would reallocate the // thread; a clear()/push_back() under them would reallocate the backing
// backing store and crash. std::move makes the publish a pointer swap. // store and crash. std::move makes the publish a pointer swap.
CSettings::DisplayModes newModes; CSettings::DisplayModes newModes;
newModes.reserve(g_settings.GetDisplayModes().size()); newModes.reserve(g_settings.GetDisplayModes().size());
for (auto& dm : g_settings.GetDisplayModes()) for (auto& dm : g_settings.GetDisplayModes())
newModes.push_back(dm); newModes.push_back(dm);
CEdid newEdid;
newEdid.Build(newModes);
AcquireSRWLockExclusive(&m_modeLock); AcquireSRWLockExclusive(&m_modeLock);
m_displayModes = std::move(newModes); m_displayModes = std::move(newModes);
m_edid = std::move(newEdid); ReleaseSRWLockExclusive(&m_modeLock);
}
void CIndirectDeviceContext::InitializeEdid()
{
AcquireSRWLockExclusive(&m_modeLock);
if (!m_edid.Size())
m_edid.Build(m_displayModes);
ReleaseSRWLockExclusive(&m_modeLock); ReleaseSRWLockExclusive(&m_modeLock);
} }
@@ -184,6 +188,7 @@ void CIndirectDeviceContext::InitAdapter()
QueryIddCxCapabilities(); QueryIddCxCapabilities();
PopulateDefaultModes(); PopulateDefaultModes();
InitializeEdid();
IDDCX_ADAPTER_CAPS caps = {}; IDDCX_ADAPTER_CAPS caps = {};
caps.Size = sizeof(caps); caps.Size = sizeof(caps);
@@ -292,10 +297,8 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
WDF_OBJECT_ATTRIBUTES attr; WDF_OBJECT_ATTRIBUTES attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, CIndirectMonitorContextWrapper); WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, CIndirectMonitorContextWrapper);
// Take a private copy of the EDID so a concurrent PopulateDefaultModes on // Take a private copy of the immutable EDID. The copy lives for the duration
// the timer thread cannot reallocate the buffer out from under // of the synchronous create call below.
// IddCxMonitorCreate. The copy lives for the duration of the synchronous
// create call below.
std::vector<BYTE> edid; std::vector<BYTE> edid;
AcquireSRWLockShared(&m_modeLock); AcquireSRWLockShared(&m_modeLock);
edid.assign(m_edid.Data(), m_edid.Data() + m_edid.Size()); edid.assign(m_edid.Data(), m_edid.Data() + m_edid.Size());

View File

@@ -140,11 +140,12 @@ private:
void DeInitLGMP(); void DeInitLGMP();
void LGMPTimer(); void LGMPTimer();
void ResendCursor() const; void ResendCursor() const;
void InitializeEdid();
// Guards m_displayModes and m_edid. The mode list is rebuilt on the LGMP // Guards m_displayModes and m_edid. The mode list is rebuilt on the LGMP
// timer thread (SetResolution) while IddCx concurrently enumerates it on its // timer thread (SetResolution) while IddCx concurrently enumerates it on its
// own callback threads (ParseMonitorDescription / MonitorQueryTargetModes / // own callback threads. The EDID is initialized once and remains immutable.
// FinishInit). Never held across an IddCx API call - snapshot then call. // Never held across an IddCx API call - snapshot then call.
mutable SRWLOCK m_modeLock = SRWLOCK_INIT; mutable SRWLOCK m_modeLock = SRWLOCK_INIT;
CSettings::DisplayModes m_displayModes; CSettings::DisplayModes m_displayModes;