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

View File

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