我目前正在寻找一种方法来使用 C++ 在带有 GPU(T4,带有网格许可证和虚拟工作站网格驱动程序)的 Windows Server 2019 VM 上设置完全自定义的分辨率。
我尝试了不同的方法来实现这一点,我可以在我的笔记本电脑上完成这项工作,但似乎对 GCP VM 有一些限制(或 Windows Server 限制)。
我尝试使用 ChangeDisplaySettings/ChangeDisplaySettingsEx (winuser.h) 来执行此操作,我可以更改为已知分辨率,但无法使其与自定义分辨率一起使用(即使使用 CDS_ENABLE_UNSAFE_MODE 也不行)。
DWORD deviceIndex = 0;
DISPLAY_DEVICE displayDevice = { 0 };
displayDevice.cb = sizeof(DISPLAY_DEVICE);
while (EnumDisplayDevices(NULL, deviceIndex, &displayDevice, 0)) {
deviceIndex++;
DEVMODE dm = { 0 };
dm.dmSize = sizeof(DEVMODE);
DEVMODE finalDm = { 0 };
finalDm.dmSize = sizeof(DEVMODE);
//Check if able to retrieve current settings
if (!EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
continue;
}
//Check if there is a difference in resolution list if UNSAFE_MODE is enabled or not (it seems to not change anything)
int result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, 0, CDS_DISABLE_UNSAFE_MODES, NULL);
std::cout << "CDS_DISABLE_UNSAFE_MODE" << std::endl;
if (result == DISP_CHANGE_SUCCESSFUL) {
for (int i = 0; EnumDisplaySettings(displayDevice.DeviceName, i, &dm) != 0; i++) {
if (dm.dmBitsPerPel == 32) {
std::cout << i << ". Found available resolution : " << dm.dmPelsWidth << " x " << dm.dmPelsHeight << " x " << dm.dmBitsPerPel << " @ " << dm.dmDisplayFrequency << std::endl;
}
}
}
result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, 0, CDS_ENABLE_UNSAFE_MODES, NULL);
std::cout << "CDS_ENABLE_UNSAFE_MODE" << std::endl;
if (result == DISP_CHANGE_SUCCESSFUL) {
for (int i = 0; EnumDisplaySettings(displayDevice.DeviceName, i, &dm) != 0; i++) {
if (dm.dmBitsPerPel == 32) {
std::cout << i << ". Found available resolution : " << dm.dmPelsWidth << " x " << dm.dmPelsHeight << " x " << dm.dmBitsPerPel << " @ " << dm.dmDisplayFrequency << std::endl;
}
}
}
std::cout << "Please enter width : ";
int width, height;
std::cin >> width;
std::cout << "Please enter height : ";
std::cin >> height;
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
if (width > height) {
dm.dmDisplayOrientation = DMDO_DEFAULT;
}
else {
dm.dmDisplayOrientation = DMDO_90;
}
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYORIENTATION;
//result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, NULL, CDS_TEST, NULL);
result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, NULL, 0, NULL);
if (result != DISP_CHANGE_SUCCESSFUL) {
std::cout << "Impossible to ChangeDisplaySettings" << endl;
}
else {
std::cout << "OK" << endl;
}
break;
}
然后我看一下 NVAPI,同样在这里,我可以让它在我的 PC 上运行,但在 GCP 虚拟机上仍然没有......我找到了一种方法让 NVAPI 在我的本地 PC 上创建和使用自定义分辨率,但是可以'不要让它再次在 GCP VM 上工作......(在此处找到代码示例)
NvAPI_Status result = NVAPI_ERROR;
NvU32 primaryDisplayId = 0;
//Testing resolution
int horizontal = 1920, vertical = 1090;
result = NvAPI_Initialize();
if (result != NVAPI_OK) {
printf("Could not initialize NvAPI");
return false;
}
MONITORINFOEX monInfo;
HMONITOR hMon;
const POINT ptZero = { 0, 0 };
// determine the location of the primary monitor
hMon = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
ZeroMemory(&monInfo, sizeof(monInfo));
monInfo.cbSize = sizeof(monInfo);
GetMonitorInfo(hMon, &monInfo);
result = NvAPI_DISP_GetGDIPrimaryDisplayId(&primaryDisplayId);
if (result != NVAPI_OK) {
printf("Could not get display ID from device");
NvAPI_Unload();
return false;
}
NvU32 deviceCount = 0;
NV_CUSTOM_DISPLAY cd[NVAPI_MAX_DISPLAYS] = { 0 };
float refreshRate = 60;
// timing computation (to get timing that suits the changes made)
NV_TIMING_FLAG flag = { 0 };
NV_TIMING_INPUT timing = { 0 };
timing.version = NV_TIMING_INPUT_VER;
timing.height = vertical;
timing.width = horizontal;
timing.rr = refreshRate;
timing.flag = flag;
timing.type = NV_TIMING_OVERRIDE_CVT_RB;
result = NvAPI_DISP_GetTiming(primaryDisplayId, &timing, &cd[0].timing);
if (result != NVAPI_OK) {
printf("Failed to get timing for display"); // failed to get custom display timing
NvAPI_Unload();
return false;
}
cd[0].width = horizontal;
cd[0].height = vertical;
cd[0].xRatio = 1;
cd[0].yRatio = 1;
cd[0].srcPartition = { 0, 0, 1.0, 1.0 };
cd[0].depth = 32;
cd[0].version = NV_CUSTOM_DISPLAY_VER;
cd[0].colorFormat = NV_FORMAT_A8R8G8B8;
//Returns NVAPI_ERROR on GCP but NVAPI_OK on my laptop
result = NvAPI_DISP_TryCustomDisplay(&primaryDisplayId, 1, cd);
if (result != NVAPI_OK) {
printf("Could not set custom resolution");
NvAPI_DISP_RevertCustomDisplayTrial(&primaryDisplayId, 1);
NvAPI_Unload();
return false;
}
else {
NvAPI_DISP_SaveCustomDisplay(&primaryDisplayId, 1, true, true);
}
这部分在我的笔记本电脑上运行良好,我可以使用新的动态分辨率(它适用于 1920x400、1920x500、1920x600),但在我的 GCP VM 上却不行,这部分:
NvAPI_DISP_TryCustomDisplay(&primaryDisplayId, 1, cd);
总是返回 NVAPI_ERROR
我发现了另一个技巧,我可以编辑这个注册表项:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Video{RANDOM_ID}\0001\NV_Modes (这是一个旧的 pdf,经过一些测试,它似乎仍然以这种方式工作)如果我使用 NVAPI 添加了一些分辨率,然后我可以通过 ChangeDisplaySettingsEx 函数设置此分辨率(它需要重新启动 GPU 驱动程序,或者重新启动 Windows 才能更改为新添加的分辨率)。但是我需要能够旋转屏幕,使用“dmDisplayOrientation”播放,并且它似乎无法再次在 GCP VM 上运行,如果我授权例如 1920x1090,我可以将分辨率设置为此,但不能使用“dmDisplayOrientation”设置 1090x1920 = DMDO_90"(即使我也授权 1090x1920...)
因此,如果有人找到了一种方法,或者对如何做到这一点有任何想法,那就太好了,我现在已经没有想法了......