fix: add explicit target mode fallback for non-standard resolutions (#594)#595
fix: add explicit target mode fallback for non-standard resolutions (#594)#595
Conversation
…594) When Windows CCD mode matching fails for non-standard resolutions (e.g. 16:10 like 2560x1600) on newly created virtual display paths, the SetDisplayConfig API returns ERROR_GEN_FAILURE because it cannot find a valid source-to-target mode mapping in its topology database. This adds a third fallback attempt in set_display_modes() that explicitly sets target mode parameters (activeSize, totalSize, vSyncFreq, hSyncFreq, pixelRate) in-place instead of clearing the target mode index and relying on Windows' automatic matching. The fallback only triggers when both existing attempts (with and without SDC_ALLOW_CHANGES) fail, so it has zero impact on environments where mode setting already works correctly. Closes #594
Summary by CodeRabbit
Walkthrough在 Windows CCD 显示配置路径中新增内部回退助手 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/platform/windows/display_device/device_modes.cpp (1)
252-275: 建议在 target_idx 无效时添加调试日志当
target_idx无效或超出范围时,代码会静默跳过目标模式更新。考虑到这是一个用于调试非标准分辨率问题的回退路径,添加日志可以帮助诊断回退失败的原因。💡 建议添加调试日志
const UINT32 target_idx { path->targetInfo.targetModeInfoIdx }; if (target_idx != DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID && target_idx < display_data->modes.size()) { auto &target_mode_info = display_data->modes[target_idx]; if (target_mode_info.infoType == DISPLAYCONFIG_MODE_INFO_TYPE_TARGET) { // ... existing code ... + } else { + BOOST_LOG(debug) << "Target mode info type mismatch for device: " << device_id; } + } else { + BOOST_LOG(debug) << "Invalid target mode index for device: " << device_id << ", idx=" << target_idx; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/platform/windows/display_device/device_modes.cpp` around lines 252 - 275, 当 target_idx 等于 DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID 或者 target_idx >= display_data->modes.size() 时当前静默跳过目标模式更新;在这条分支处(在对 target_idx 的 if 判断外或其 else 分支里)添加一条调试日志,记录 target_idx 值、display_data->modes.size() 以及相关 mode(如 mode.resolution.width/height 和 mode.refresh_rate.numerator/denominator)的信息,以便追踪为何未应用目标模式(引用符号:target_idx、DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID、display_data->modes、mode、path->targetInfo.targetModeInfoIdx、target_mode_info)。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/platform/windows/display_device/device_modes.cpp`:
- Around line 252-275: 当 target_idx 等于
DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID 或者 target_idx >=
display_data->modes.size() 时当前静默跳过目标模式更新;在这条分支处(在对 target_idx 的 if 判断外或其 else
分支里)添加一条调试日志,记录 target_idx 值、display_data->modes.size() 以及相关 mode(如
mode.resolution.width/height 和
mode.refresh_rate.numerator/denominator)的信息,以便追踪为何未应用目标模式(引用符号:target_idx、DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID、display_data->modes、mode、path->targetInfo.targetModeInfoIdx、target_mode_info)。
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f28aef29-aa7b-4215-a010-562a132e4865
📒 Files selected for processing (1)
src/platform/windows/display_device/device_modes.cpp
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,c,h}
⚙️ CodeRabbit configuration file
src/**/*.{cpp,c,h}: Sunshine 核心 C++ 源码,自托管游戏串流服务器。审查要点:内存安全、 线程安全、RAII 资源管理、安全漏洞。注意预处理宏控制的平台相关代码。
Files:
src/platform/windows/display_device/device_modes.cpp
src/platform/**
⚙️ CodeRabbit configuration file
src/platform/**: 平台抽象层代码(Windows/Linux/macOS)。确保各平台实现一致, 注意 Windows API 调用的错误处理和资源释放。
Files:
src/platform/windows/display_device/device_modes.cpp
🔇 Additional comments (4)
src/platform/windows/display_device/device_modes.cpp (4)
277-290: 桌面图像信息更新逻辑正确正确地只更新了
right和bottom字段(left和top保持为 0),这符合 Windows RECT 结构的原点坐标惯例。
443-453: 控制流变更正确,遵循现有模式新增的回退逻辑正确地遵循了函数中已建立的模式:
- 仅在推荐模式和严格模式都失败后才尝试
- 在尝试前记录信息日志
- 成功后验证模式是否匹配
- 失败时继续执行原有的恢复逻辑
这种渐进式回退策略确保了现有工作场景不受影响。
299-301: 日志级别使用合理这里使用
warning而非error级别是合理的,因为这是回退路径,失败后还会尝试恢复原始模式。这与主路径do_set_modes中使用error级别形成了适当的区分。
262-271: 这些信号参数计算是 Windows 虚拟显示驱动 (VDD) 的标准做法,与 Microsoft 官方 IddCx 示例代码一致,无需进一步验证。设置activeSize与totalSize相同(无消隐间隔),以及使用hSyncFreq = vSync * Height和pixelRate = vSync * Width * Height / Denominator的公式,都是 VDD 实现的规范方式。
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/platform/windows/display_device/device_modes.cpp`:
- Around line 429-438: The code currently returns false immediately when
do_set_modes(modes, allow_changes) fails (e.g., ERROR_GEN_FAILURE), so the later
fallback do_set_modes_with_explicit_target(modes) only runs for the
"applied-but-mismatch" case; change set_display_modes() so that both failure
cases (initial do_set_modes returned false) and the "applied but
all_modes_match() is false" path flow into the same fallback chain: attempt
do_set_modes_with_explicit_target(modes), then re-query via
get_current_display_modes(device_ids) and check all_modes_match(current_modes)
before returning; ensure you do not early-return on the first failure, propagate
and log Windows error codes consistently, and keep resource cleanup/error
handling identical to the existing mismatch path (use same logging and return
semantics only after fallback finishes).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 02d4a3b8-cb7e-49ae-a4a5-1e4cbe223299
📒 Files selected for processing (1)
src/platform/windows/display_device/device_modes.cpp
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Windows
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,c,h}
⚙️ CodeRabbit configuration file
src/**/*.{cpp,c,h}: Sunshine 核心 C++ 源码,自托管游戏串流服务器。审查要点:内存安全、 线程安全、RAII 资源管理、安全漏洞。注意预处理宏控制的平台相关代码。
Files:
src/platform/windows/display_device/device_modes.cpp
src/platform/**
⚙️ CodeRabbit configuration file
src/platform/**: 平台抽象层代码(Windows/Linux/macOS)。确保各平台实现一致, 注意 Windows API 调用的错误处理和资源释放。
Files:
src/platform/windows/display_device/device_modes.cpp
🔇 Additional comments (1)
src/platform/windows/display_device/device_modes.cpp (1)
233-275: 这里的targetModeInfoIdx读取在本代码库中是正确的,无需额外的DISPLAYCONFIG_PATH_SUPPORT_VIRTUAL_MODE门控。根据
windows_utils.cpp的代码文档(第 494-499、516-521 行等),微软文档虽然声称该字段仅在设置DISPLAYCONFIG_PATH_SUPPORT_VIRTUAL_MODE标志时有效,但这个结论经验证是"BS"(文档的复制粘贴错误)。实际的联合体可访问性取决于查询时是否指定了QDC_VIRTUAL_MODE_AWARE标志,本函数已满足此要求(query_display_config()使用该标志)。代码已通过以下方式防御不可用的索引:
target_idx != DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID检查target_idx < display_data->modes.size()边界检查infoType == DISPLAYCONFIG_MODE_INFO_TYPE_TARGET类型验证关于桌面图像同步的顾虑在本代码库中不适用,因为代码中无相关的
DISPLAYCONFIG_DESKTOP_IMAGE_INFO处理。
| // Fallback: explicitly set target mode parameters instead of relying on Windows CCD mode matching. | ||
| // This helps with non-standard resolutions (e.g. 16:10 like 2560x1600) on newly created virtual | ||
| // display paths where the CCD topology database has no matching mode entries. | ||
| BOOST_LOG(info) << "CCD mode matching failed, trying to set modes with explicit target mode parameters."; | ||
| if (do_set_modes_with_explicit_target(modes)) { | ||
| current_modes = get_current_display_modes(device_ids); | ||
| if (!current_modes.empty() && all_modes_match(current_modes)) { | ||
| return true; | ||
| } | ||
| } |
There was a problem hiding this comment.
这个回退链在 SetDisplayConfig 直接失败时走不到。
#594 要修的是第一次 do_set_modes(modes, allow_changes) 就因为 ERROR_GEN_FAILURE 失败的场景,但这里的第三层回退只会在前一次“调用成功但结果不匹配”时才执行。当前 set_display_modes() 在 Line 384-387 直接 return false,而 src/platform/windows/display_device/settings.cpp:250-256 会把这个失败继续上抛,所以严格重试和新增的显式 target 回退都不会覆盖到 PR 的主问题。请把“首次应用失败”和“应用后校验不匹配”统一纳入同一条回退链,最后再统一失败返回。
As per coding guidelines src/platform/**: 平台抽象层代码(Windows/Linux/macOS)。确保各平台实现一致, 注意 Windows API 调用的错误处理和资源释放。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/platform/windows/display_device/device_modes.cpp` around lines 429 - 438,
The code currently returns false immediately when do_set_modes(modes,
allow_changes) fails (e.g., ERROR_GEN_FAILURE), so the later fallback
do_set_modes_with_explicit_target(modes) only runs for the
"applied-but-mismatch" case; change set_display_modes() so that both failure
cases (initial do_set_modes returned false) and the "applied but
all_modes_match() is false" path flow into the same fallback chain: attempt
do_set_modes_with_explicit_target(modes), then re-query via
get_current_display_modes(device_ids) and check all_modes_match(current_modes)
before returning; ensure you do not early-return on the first failure, propagate
and log Windows error codes consistently, and keep resource cleanup/error
handling identical to the existing mismatch path (use same logging and return
semantics only after fallback finishes).
Problem
When streaming to devices with non-standard resolutions like 2560x1600 (16:10 aspect ratio), the
SetDisplayConfigAPI returnsERROR_GEN_FAILUREbecause Windows CCD (Connecting and Configuring Displays) cannot find a valid source-to-target mode mapping in its topology database for newly created virtual display paths.The failure occurs in
set_display_modes()indevice_modes.cpp:SDC_ALLOW_CHANGES): Windows adjusts the mode to something else (e.g., 2560x1440), fuzzy compare detects mismatchSetDisplayConfigreturnsERROR_GEN_FAILUREbecause the CCD database has no mapping for the non-standard resolutionStandard resolutions like 2560x1440 work because Windows has built-in mode matching support for common 16:9 modes.
Root Cause
The current code clears the
targetModeInfoIdxanddesktopModeInfoIdxin the path, telling Windows to automatically match source mode to target mode. For newly created VDD paths with non-standard resolutions (especially when the CCD topology was itself just created via fallback), Windows' automatic mode matching algorithm fails.Solution
Add a third fallback attempt in
set_display_modes()via a newdo_set_modes_with_explicit_target()function that:activeSize,totalSize,vSyncFreq,hSyncFreq,pixelRate)This bypasses Windows CCD's automatic mode matching entirely by providing a complete, self-consistent display configuration.
Impact
device_modes.cppis modifiedCloses #594