Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Edid Resolution parsing, working draft #325

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Edid Resolution parsing, working draft
By adding a user_edid.bin in  VDD's install path and set <parse_edid_res> to true in vdd_settings.xml. It will replace all resolutions from vdd_settings.xml with what it manage to grab from the edid file. Needs more testing
  • Loading branch information
zjoasan committed Mar 21, 2025
commit b43f13092dc3f28fbf67c27c801ed2935effa8e3
240 changes: 96 additions & 144 deletions Virtual Display Driver (HDR)/MttVDD/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Copyright (c) Microsoft Corporation
--*/

#include "Driver.h"
#include "edid_parser.cpp"
//#include "Driver.tmh"
#include<fstream>
#include<sstream>
Expand Down Expand Up @@ -1492,13 +1493,15 @@ vector<string> split(string& input, char delimiter)
void loadSettings() {
const wstring settingsname = confpath + L"\\vdd_settings.xml";
const wstring& filename = settingsname;
bool parseEdidRes = false; // Default to false unless specified in XML

if (PathFileExistsW(filename.c_str())) {
CComPtr<IStream> pStream;
CComPtr<IXmlReader> pReader;
HRESULT hr = SHCreateStreamOnFileW(filename.c_str(), STGM_READ, &pStream);
if (FAILED(hr)) {
vddlog("e", "Loading Settings: Failed to create file stream.");
return;
return;
}
hr = CreateXmlReader(__uuidof(IXmlReader), (void**)&pReader, NULL);
if (FAILED(hr)) {
Expand Down Expand Up @@ -1528,16 +1531,12 @@ void loadSettings() {
switch (nodeType) {
case XmlNodeType_Element:
hr = pReader->GetLocalName(&pwszLocalName, &cwchLocalName);
if (FAILED(hr)) {
return;
}
if (FAILED(hr)) return;
currentElement = wstring(pwszLocalName, cwchLocalName);
break;
case XmlNodeType_Text:
hr = pReader->GetValue(&pwszValue, &cwchValue);
if (FAILED(hr)) {
return;
}
if (FAILED(hr)) return;
if (currentElement == L"count") {
monitorcount = stoi(wstring(pwszValue, cwchValue));
if (monitorcount == 0) {
Expand All @@ -1550,25 +1549,18 @@ void loadSettings() {
}
else if (currentElement == L"width") {
width = wstring(pwszValue, cwchValue);
if (width.empty()) {
width = L"800";
}
if (width.empty()) width = L"800";
}
else if (currentElement == L"height") {
height = wstring(pwszValue, cwchValue);
if (height.empty()) {
height = L"600";
}
if (height.empty()) height = L"600";
resolutions.insert(make_tuple(stoi(width), stoi(height)));
}
else if (currentElement == L"refresh_rate") {
refreshRate = wstring(pwszValue, cwchValue);
if (refreshRate.empty()) {
refreshRate = L"30";
}
if (refreshRate.empty()) refreshRate = L"30";
int vsync_num, vsync_den;
float_to_vsync(stof(refreshRate), vsync_num, vsync_den);

res.push_back(make_tuple(stoi(width), stoi(height), vsync_num, vsync_den));
stringstream ss;
ss << "Added: " << stoi(width) << "x" << stoi(height) << " @ " << vsync_num << "/" << vsync_den << "Hz";
Expand All @@ -1577,155 +1569,115 @@ void loadSettings() {
else if (currentElement == L"g_refresh_rate") {
globalRefreshRates.push_back(stoi(wstring(pwszValue, cwchValue)));
}
else if (currentElement == L"parse_edid_res") {
wstring value(pwszValue, cwchValue);
parseEdidRes = (value == L"true" || value == L"1"); // Accept "true" or "1"
}
break;
}
}

/*
* This is for res testing, stores each resolution then iterates through each global adding a res for each one
*

for (const auto& resTuple : resolutions) {
stringstream ss;
ss << get<0>(resTuple) << "x" << get<1>(resTuple);
vddlog("t", ss.str().c_str());
}

for (const auto& globalRate : globalRefreshRates) {
stringstream ss;
ss << globalRate << " Hz";
vddlog("t", ss.str().c_str());
}
*/

for (int globalRate : globalRefreshRates) {
for (const auto& resTuple : resolutions) {
int global_width = get<0>(resTuple);
int global_height = get<1>(resTuple);

int vsync_num, vsync_den;
float_to_vsync(static_cast<float>(globalRate), vsync_num, vsync_den);
res.push_back(make_tuple(global_width, global_height, vsync_num, vsync_den));
}
}

/*
* logging all resolutions after added global
*
for (const auto& tup : res) {
stringstream ss;
ss << "("
<< get<0>(tup) << ", "
<< get<1>(tup) << ", "
<< get<2>(tup) << ", "
<< get<3>(tup) << ")";
vddlog("t", ss.str().c_str());
}

*/


numVirtualDisplays = monitorcount;
gpuname = gpuFriendlyName;
monitorModes = res;
vddlog("i","Using vdd_settings.xml");
return;
vddlog("i", "Using vdd_settings.xml");
}
const wstring optionsname = confpath + L"\\option.txt";
ifstream ifs(optionsname);
if (ifs.is_open()) {
string line;
if (getline(ifs, line) && !line.empty()) {
numVirtualDisplays = stoi(line);
vector<tuple<int, int, int, int>> res;

while (getline(ifs, line)) {
vector<string> strvec = split(line, ',');
if (strvec.size() == 3 && strvec[0].substr(0, 1) != "#") {
int vsync_num, vsync_den;
float_to_vsync(stof(strvec[2]), vsync_num, vsync_den);
res.push_back({ stoi(strvec[0]), stoi(strvec[1]), vsync_num, vsync_den });
}
}

vddlog("i", "Using option.txt");
monitorModes = res;
for (const auto& mode : res) {
int width, height, vsync_num, vsync_den;
tie(width, height, vsync_num, vsync_den) = mode;
stringstream ss;
ss << "Resolution: " << width << "x" << height << " @ " << vsync_num << "/" << vsync_den << "Hz";
vddlog("d", ss.str().c_str());
}
return;
} else {
vddlog("w", "option.txt is empty or the first line is invalid. Enabling Fallback");
}
}


numVirtualDisplays = 1;
vector<tuple<int, int, int, int>> res;
vector<tuple<int, int, float>> fallbackRes = {
{800, 600, 30.0f},
{800, 600, 60.0f},
{800, 600, 90.0f},
{800, 600, 120.0f},
{800, 600, 144.0f},
{800, 600, 165.0f},
{1280, 720, 30.0f},
{1280, 720, 60.0f},
{1280, 720, 90.0f},
{1280, 720, 130.0f},
{1280, 720, 144.0f},
{1280, 720, 165.0f},
{1366, 768, 30.0f},
{1366, 768, 60.0f},
{1366, 768, 90.0f},
{1366, 768, 120.0f},
{1366, 768, 144.0f},
{1366, 768, 165.0f},
{1920, 1080, 30.0f},
{1920, 1080, 60.0f},
{1920, 1080, 90.0f},
{1920, 1080, 120.0f},
{1920, 1080, 144.0f},
{1920, 1080, 165.0f},
{2560, 1440, 30.0f},
{2560, 1440, 60.0f},
{2560, 1440, 90.0f},
{2560, 1440, 120.0f},
{2560, 1440, 144.0f},
{2560, 1440, 165.0f},
{3840, 2160, 30.0f},
{3840, 2160, 60.0f},
{3840, 2160, 90.0f},
{3840, 2160, 120.0f},
{3840, 2160, 144.0f},
{3840, 2160, 165.0f}
};

vddlog("i", "Loading Fallback - no settings found");

for (const auto& mode : fallbackRes) {
int width, height;
float refreshRate;
tie(width, height, refreshRate) = mode;

int vsync_num, vsync_den;
float_to_vsync(refreshRate, vsync_num, vsync_den);

stringstream ss;
res.push_back(make_tuple(width, height, vsync_num, vsync_den));
else {
const wstring optionsname = confpath + L"\\option.txt";
ifstream ifs(optionsname);
if (ifs.is_open()) {
string line;
if (getline(ifs, line) && !line.empty()) {
numVirtualDisplays = stoi(line);
vector<tuple<int, int, int, int>> res;

while (getline(ifs, line)) {
vector<string> strvec = split(line, ',');
if (strvec.size() == 3 && strvec[0].substr(0, 1) != "#") {
int vsync_num, vsync_den;
float_to_vsync(stof(strvec[2]), vsync_num, vsync_den);
res.push_back({ stoi(strvec[0]), stoi(strvec[1]), vsync_num, vsync_den });
}
}

vddlog("i", "Using option.txt");
monitorModes = res;
for (const auto& mode : res) {
int width, height, vsync_num, vsync_den;
tie(width, height, vsync_num, vsync_den) = mode;
stringstream ss;
ss << "Resolution: " << width << "x" << height << " @ " << vsync_num << "/" << vsync_den << "Hz";
vddlog("d", ss.str().c_str());
}
}
else {
vddlog("w", "option.txt is empty or the first line is invalid. Enabling Fallback");
}
ifs.close();
}
else {
numVirtualDisplays = 1;
vector<tuple<int, int, int, int>> res;
vector<tuple<int, int, float>> fallbackRes = {
{800, 600, 30.0f}, {800, 600, 60.0f}, {800, 600, 90.0f}, {800, 600, 120.0f}, {800, 600, 144.0f}, {800, 600, 165.0f},
{1280, 720, 30.0f}, {1280, 720, 60.0f}, {1280, 720, 90.0f}, {1280, 720, 130.0f}, {1280, 720, 144.0f}, {1280, 720, 165.0f},
{1366, 768, 30.0f}, {1366, 768, 60.0f}, {1366, 768, 90.0f}, {1366, 768, 120.0f}, {1366, 768, 144.0f}, {1366, 768, 165.0f},
{1920, 1080, 30.0f}, {1920, 1080, 60.0f}, {1920, 1080, 90.0f}, {1920, 1080, 120.0f}, {1920, 1080, 144.0f}, {1920, 1080, 165.0f},
{2560, 1440, 30.0f}, {2560, 1440, 60.0f}, {2560, 1440, 90.0f}, {2560, 1440, 120.0f}, {2560, 1440, 144.0f}, {2560, 1440, 165.0f},
{3840, 2160, 30.0f}, {3840, 2160, 60.0f}, {3840, 2160, 90.0f}, {3840, 2160, 120.0f}, {3840, 2160, 144.0f}, {3840, 2160, 165.0f}
};

ss << "Resolution: " << width << "x" << height << " @ " << vsync_num << "/" << vsync_den << "Hz";
vddlog("d", ss.str().c_str());
vddlog("i", "Loading Fallback - no settings found");
for (const auto& mode : fallbackRes) {
int width, height;
float refreshRate;
tie(width, height, refreshRate) = mode;
int vsync_num, vsync_den;
float_to_vsync(refreshRate, vsync_num, vsync_den);
res.push_back(make_tuple(width, height, vsync_num, vsync_den));
stringstream ss;
ss << "Resolution: " << width << "x" << height << " @ " << vsync_num << "/" << vsync_den << "Hz";
vddlog("d", ss.str().c_str());
}
monitorModes = res;
}
}

monitorModes = res;
return;

// EDID override: Only if <parse_edid_res>true</parse_edid_res> in XML and user_edid.bin exists
if (parseEdidRes) {
const wstring edidname = confpath + L"\\user_edid.bin";
if (PathFileExistsW(edidname.c_str())) {
try {
string edidPath(edidname.begin(), edidname.end()); // Convert wstring to string
monitorModes = EdidParser::load_and_parse_edid(edidPath);
vddlog("i", "Overriding monitor modes with user_edid.bin (parse_edid_res = true)");
for (const auto& mode : monitorModes) {
int width, height, vsync_num, vsync_den;
tie(width, height, vsync_num, vsync_den) = mode;
stringstream ss;
ss << "EDID Resolution: " << width << "x" << height << " @ " << vsync_num << "/" << vsync_den << "Hz";
vddlog("d", ss.str().c_str());
}
}
catch (const std::exception& e) {
vddlog("e", ("EDID parsing failed: " + string(e.what())).c_str());
// Keep existing monitorModes (XML, option.txt, or fallback) on failure
}
}
else {
vddlog("w", "parse_edid_res is true, but user_edid.bin not found; keeping current modes");
}
}
}

_Use_decl_annotations_
Expand Down
2 changes: 2 additions & 0 deletions Virtual Display Driver (HDR)/MttVDD/MttVDD.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@
<EnablePREfast>true</EnablePREfast>
<AdditionalOptions>/D_ATL_NO_WIN_SUPPORT /DUMDF_DRIVER /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=10 /DIDDCX_MINIMUM_VERSION_REQUIRED=3 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\Common\Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>false</TreatWarningAsError>
<LanguageStandard>stdcpp14</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib</AdditionalDependencies>
Expand Down
Loading
Loading