Skip to content

Commit fc49947

Browse files
committed
Fix multi-monitor swapchain threading for issue #449
1 parent 052f0aa commit fc49947

2 files changed

Lines changed: 119 additions & 77 deletions

File tree

Virtual Display Driver (HDR)/MttVDD/Driver.cpp

Lines changed: 114 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,27 @@ bool preventManufacturerSpoof = false;
102102
bool edidCeaOverride = false;
103103
bool sendLogsThroughPipe = true;
104104

105+
constexpr DISPLAYCONFIG_VIDEO_SIGNAL_INFO dispinfo(UINT32 h, UINT32 v, UINT32 rn, UINT32 rd);
106+
107+
namespace
108+
{
109+
void RebuildKnownMonitorModesCache()
110+
{
111+
s_KnownMonitorModes2.clear();
112+
s_KnownMonitorModes2.reserve(monitorModes.size());
113+
114+
for (const auto& mode : monitorModes)
115+
{
116+
s_KnownMonitorModes2.push_back(
117+
dispinfo(
118+
std::get<0>(mode),
119+
std::get<1>(mode),
120+
std::get<2>(mode),
121+
std::get<3>(mode)));
122+
}
123+
}
124+
}
125+
105126
//Mouse settings
106127
bool alphaCursorSupport = true;
107128
int CursorMaxX = 128;
@@ -1325,6 +1346,7 @@ bool ApplyEdidProfile(const EdidProfileData& profile) {
13251346
// Validate the final mode list
13261347
if (ValidateModeList(finalModes)) {
13271348
monitorModes = finalModes;
1349+
RebuildKnownMonitorModesCache();
13281350

13291351
stringstream ss;
13301352
ss << "Enhanced mode management completed:\n"
@@ -1461,6 +1483,14 @@ void SendToPipe(const std::string& logMessage) {
14611483
}
14621484

14631485
void vddlog(const char* type, const char* message) {
1486+
if (!logsEnabled) {
1487+
return;
1488+
}
1489+
1490+
if (type != nullptr && type[0] == 'd' && !debugLogs) {
1491+
return;
1492+
}
1493+
14641494
FILE* logFile;
14651495
wstring logsDir = confpath + L"\\Logs";
14661496

@@ -1473,30 +1503,8 @@ void vddlog(const char* type, const char* message) {
14731503

14741504
wstring logPath = logsDir + L"\\log_" + date_str + L".txt";
14751505

1476-
if (logsEnabled) {
1477-
if (!CreateDirectoryW(logsDir.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
1478-
//Just any errors here
1479-
}
1480-
}
1481-
else {
1482-
WIN32_FIND_DATAW findFileData;
1483-
HANDLE hFind = FindFirstFileW((logsDir + L"\\*").c_str(), &findFileData);
1484-
1485-
if (hFind != INVALID_HANDLE_VALUE) {
1486-
do {
1487-
const wstring fileOrDir = findFileData.cFileName;
1488-
if (fileOrDir != L"." && fileOrDir != L"..") {
1489-
wstring filePath = logsDir + L"\\" + fileOrDir;
1490-
if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1491-
DeleteFileW(filePath.c_str());
1492-
}
1493-
}
1494-
} while (FindNextFileW(hFind, &findFileData) != 0);
1495-
FindClose(hFind);
1496-
}
1497-
1498-
RemoveDirectoryW(logsDir.c_str());
1499-
return;
1506+
if (!CreateDirectoryW(logsDir.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
1507+
// Best effort only.
15001508
}
15011509

15021510
string narrow_logPath = WStringToString(logPath);
@@ -1506,8 +1514,9 @@ void vddlog(const char* type, const char* message) {
15061514
stringstream ss;
15071515
ss << put_time(&tm_buf, "%Y-%m-%d %X");
15081516

1517+
const char logTypeCode = (type != nullptr) ? type[0] : '\0';
15091518
string logType;
1510-
switch (type[0]) {
1519+
switch (logTypeCode) {
15111520
case 'e':
15121521
logType = "ERROR";
15131522
break;
@@ -1534,21 +1543,13 @@ void vddlog(const char* type, const char* message) {
15341543
break;
15351544
}
15361545

1537-
bool shouldLog = true;
1538-
if (logType == "DEBUG" && !debugLogs) {
1539-
shouldLog = false;
1540-
}
1541-
if (shouldLog) {
1542-
fprintf(logFile, "[%s] [%s] %s\n", ss.str().c_str(), logType.c_str(), message);
1543-
}
1546+
fprintf(logFile, "[%s] [%s] %s\n", ss.str().c_str(), logType.c_str(), message);
15441547

15451548
fclose(logFile);
15461549

15471550
if (sendLogsThroughPipe && g_pipeHandle != INVALID_HANDLE_VALUE) {
15481551
string logMessage = ss.str() + " [" + logType + "] " + message + "\n";
1549-
DWORD bytesWritten;
1550-
DWORD logMessageSize = static_cast<DWORD>(logMessage.size());
1551-
WriteFile(g_pipeHandle, logMessage.c_str(), logMessageSize, &bytesWritten, NULL);
1552+
SendToPipe(logMessage);
15521553
}
15531554
}
15541555
}
@@ -2685,6 +2686,7 @@ void loadSettings() {
26852686
numVirtualDisplays = monitorcount;
26862687
gpuname = gpuFriendlyName;
26872688
monitorModes = res;
2689+
RebuildKnownMonitorModesCache();
26882690

26892691
// === APPLY EDID INTEGRATION ===
26902692
if (edidIntegrationEnabled && autoConfigureFromEdid) {
@@ -2726,6 +2728,7 @@ void loadSettings() {
27262728

27272729
vddlog("i", "Using option.txt");
27282730
monitorModes = res;
2731+
RebuildKnownMonitorModesCache();
27292732
for (const auto& mode : res) {
27302733
int width, height, vsync_num, vsync_den;
27312734
tie(width, height, vsync_num, vsync_den) = mode;
@@ -2800,6 +2803,7 @@ void loadSettings() {
28002803
}
28012804

28022805
monitorModes = res;
2806+
RebuildKnownMonitorModesCache();
28032807
return;
28042808

28052809
}
@@ -3328,33 +3332,46 @@ void SwapChainProcessor::RunCore()
33283332
logStream.str("");
33293333
if (hr == E_PENDING)
33303334
{
3331-
// We must wait for a new buffer
3332-
HANDLE WaitHandles[] =
3335+
HANDLE waitHandles[2] = {};
3336+
DWORD waitHandleCount = 0;
3337+
3338+
if (m_hAvailableBufferEvent != nullptr && m_hAvailableBufferEvent != INVALID_HANDLE_VALUE)
3339+
{
3340+
waitHandles[waitHandleCount++] = m_hAvailableBufferEvent;
3341+
}
3342+
3343+
if (m_hTerminateEvent.Get())
33333344
{
3334-
m_hAvailableBufferEvent,
3335-
m_hTerminateEvent.Get()
3336-
};
3337-
DWORD WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles), WaitHandles, FALSE, 100);
3345+
waitHandles[waitHandleCount++] = m_hTerminateEvent.Get();
3346+
}
3347+
3348+
if (waitHandleCount == 0)
3349+
{
3350+
vddlog("e", "No valid wait handles available while waiting for the next frame.");
3351+
break;
3352+
}
3353+
3354+
DWORD WaitResult = WaitForMultipleObjects(waitHandleCount, waitHandles, FALSE, INFINITE);
33383355

33393356
logStream << "Buffer acquisition pending. WaitResult: " << WaitResult;
33403357

3341-
if (WaitResult == WAIT_OBJECT_0 || WaitResult == WAIT_TIMEOUT)
3358+
if (WaitResult == WAIT_OBJECT_0)
33423359
{
3343-
// We have a new buffer, so try the AcquireBuffer again
3344-
//vddlog("d", "New buffer trying aquire new buffer");
33453360
continue;
33463361
}
3347-
else if (WaitResult == WAIT_OBJECT_0 + 1)
3362+
else if (waitHandleCount > 1 && WaitResult == WAIT_OBJECT_0 + 1)
3363+
{
3364+
logStream << "Terminate event signaled. Exiting loop.";
3365+
break;
3366+
}
3367+
else if (waitHandleCount == 1 && waitHandles[0] == m_hTerminateEvent.Get() && WaitResult == WAIT_OBJECT_0)
33483368
{
3349-
// We need to terminate
33503369
logStream << "Terminate event signaled. Exiting loop.";
3351-
//vddlog("d", logStream.str().c_str());
33523370
break;
33533371
}
33543372
else
33553373
{
3356-
// The wait was cancelled or something unexpected happened
3357-
hr = HRESULT_FROM_WIN32(WaitResult);
3374+
hr = HRESULT_FROM_WIN32(WaitResult == WAIT_FAILED ? GetLastError() : WaitResult);
33583375
logStream << "Unexpected wait result. HRESULT: " << hr;
33593376
vddlog("e", logStream.str().c_str());
33603377
break;
@@ -3649,14 +3666,18 @@ IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) :
36493666
IndirectDeviceContext::~IndirectDeviceContext()
36503667
{
36513668
stringstream logStream;
3669+
std::map<IDDCX_MONITOR, std::unique_ptr<SwapChainProcessor>> processingThreads;
36523670

3653-
logStream << "Destroying IndirectDeviceContext. Resetting processing thread.";
3671+
logStream << "Destroying IndirectDeviceContext. Releasing per-monitor processing threads.";
36543672
vddlog("d", logStream.str().c_str());
36553673

3656-
m_ProcessingThread.reset();
3674+
{
3675+
std::lock_guard<std::mutex> lock(m_ProcessingThreadsMutex);
3676+
processingThreads.swap(m_ProcessingThreads);
3677+
}
36573678

36583679
logStream.str("");
3659-
logStream << "Processing thread has been reset.";
3680+
logStream << "Released " << processingThreads.size() << " monitor processing thread(s).";
36603681
vddlog("d", logStream.str().c_str());
36613682
}
36623683

@@ -3852,15 +3873,8 @@ void IndirectDeviceContext::CreateMonitor(unsigned int index) {
38523873
}
38533874
}
38543875

3855-
void IndirectDeviceContext::AssignSwapChain(IDDCX_MONITOR& Monitor, IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent)
3876+
void IndirectDeviceContext::AssignSwapChain(IDDCX_MONITOR Monitor, IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent)
38563877
{
3857-
// Properly wait for existing thread to complete before creating new one
3858-
if (m_ProcessingThread) {
3859-
vddlog("d", "Waiting for existing processing thread to complete before reassignment.");
3860-
m_ProcessingThread.reset(); // This will call destructor which waits for thread completion
3861-
vddlog("d", "Existing processing thread completed.");
3862-
}
3863-
38643878
// Only cleanup expired devices periodically, not on every assignment
38653879
static int assignmentCount = 0;
38663880
if (++assignmentCount % 10 == 0) {
@@ -3876,8 +3890,22 @@ void IndirectDeviceContext::AssignSwapChain(IDDCX_MONITOR& Monitor, IDDCX_SWAPCH
38763890
}
38773891
else
38783892
{
3879-
vddlog("d", "Creating a new swap-chain processing thread.");
3880-
m_ProcessingThread.reset(new SwapChainProcessor(SwapChain, Device, NewFrameEvent));
3893+
std::unique_ptr<SwapChainProcessor> previousProcessor;
3894+
auto newProcessor = std::make_unique<SwapChainProcessor>(SwapChain, Device, NewFrameEvent);
3895+
3896+
{
3897+
std::lock_guard<std::mutex> lock(m_ProcessingThreadsMutex);
3898+
auto& processorSlot = m_ProcessingThreads[Monitor];
3899+
previousProcessor = std::move(processorSlot);
3900+
processorSlot = std::move(newProcessor);
3901+
}
3902+
3903+
if (previousProcessor) {
3904+
vddlog("d", "Replaced existing processing thread for this monitor only.");
3905+
}
3906+
else {
3907+
vddlog("d", "Created a new processing thread for this monitor.");
3908+
}
38813909

38823910
if (hardwareCursor){
38833911
HANDLE mouseEvent = CreateEventA(
@@ -3930,11 +3958,28 @@ void IndirectDeviceContext::AssignSwapChain(IDDCX_MONITOR& Monitor, IDDCX_SWAPCH
39303958
}
39313959

39323960

3933-
void IndirectDeviceContext::UnassignSwapChain()
3961+
void IndirectDeviceContext::UnassignSwapChain(IDDCX_MONITOR Monitor)
39343962
{
3935-
// Stop processing the last swap-chain
3936-
vddlog("i", "Unasigning Swapchain. Processing will be stopped.");
3937-
m_ProcessingThread.reset();
3963+
std::unique_ptr<SwapChainProcessor> processorToStop;
3964+
3965+
{
3966+
std::lock_guard<std::mutex> lock(m_ProcessingThreadsMutex);
3967+
auto it = m_ProcessingThreads.find(Monitor);
3968+
if (it != m_ProcessingThreads.end())
3969+
{
3970+
processorToStop = std::move(it->second);
3971+
m_ProcessingThreads.erase(it);
3972+
}
3973+
}
3974+
3975+
if (processorToStop)
3976+
{
3977+
vddlog("i", "Unassigning swapchain for one monitor. Its processing thread will be stopped.");
3978+
}
3979+
else
3980+
{
3981+
vddlog("w", "UnassignSwapChain called for a monitor without an active processing thread.");
3982+
}
39383983
}
39393984

39403985
#pragma endregion
@@ -3993,9 +4038,7 @@ NTSTATUS VirtualDisplayDriverParseMonitorDescription(const IDARG_IN_PARSEMONITOR
39934038
logStream << "Parsing monitor description. Input buffer count: " << pInArgs->MonitorModeBufferInputCount;
39944039
vddlog("d", logStream.str().c_str());
39954040

3996-
for (int i = 0; i < monitorModes.size(); i++) {
3997-
s_KnownMonitorModes2.push_back(dispinfo(std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]), std::get<3>(monitorModes[i])));
3998-
}
4041+
RebuildKnownMonitorModesCache();
39994042
pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size();
40004043

40014044
logStream.str("");
@@ -4194,7 +4237,7 @@ NTSTATUS VirtualDisplayDriverMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObjec
41944237
logStream << "Unassigning swap chain for monitor object: " << MonitorObject;
41954238
vddlog("d", logStream.str().c_str());
41964239
auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject);
4197-
pContext->pContext->UnassignSwapChain();
4240+
pContext->pContext->UnassignSwapChain(MonitorObject);
41984241
vddlog("d", "Swap chain unassigned successfully.");
41994242
return STATUS_SUCCESS;
42004243
}
@@ -4360,9 +4403,7 @@ NTSTATUS VirtualDisplayDriverEvtIddCxParseMonitorDescription2(
43604403
}
43614404
vddlog("d", logStream.str().c_str());
43624405

4363-
for (int i = 0; i < monitorModes.size(); i++) {
4364-
s_KnownMonitorModes2.push_back(dispinfo(std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]), std::get<3>(monitorModes[i])));
4365-
}
4406+
RebuildKnownMonitorModesCache();
43664407
pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size();
43674408

43684409
if (pInArgs->MonitorModeBufferInputCount < monitorModes.size())

Virtual Display Driver (HDR)/MttVDD/Driver.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ namespace Microsoft
115115

116116
void CreateMonitor(unsigned int index);
117117

118-
void AssignSwapChain(IDDCX_MONITOR& Monitor, IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent);
119-
void UnassignSwapChain();
118+
void AssignSwapChain(IDDCX_MONITOR Monitor, IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent);
119+
void UnassignSwapChain(IDDCX_MONITOR Monitor);
120120

121121
protected:
122122

@@ -125,7 +125,8 @@ namespace Microsoft
125125
IDDCX_MONITOR m_Monitor;
126126
IDDCX_MONITOR m_Monitor2;
127127

128-
std::unique_ptr<SwapChainProcessor> m_ProcessingThread;
128+
std::map<IDDCX_MONITOR, std::unique_ptr<SwapChainProcessor>> m_ProcessingThreads;
129+
std::mutex m_ProcessingThreadsMutex;
129130

130131
public:
131132
static const DISPLAYCONFIG_VIDEO_SIGNAL_INFO s_KnownMonitorModes[];
@@ -138,4 +139,4 @@ namespace Microsoft
138139
static void CleanupExpiredDevices();
139140
};
140141
}
141-
}
142+
}

0 commit comments

Comments
 (0)