Skip to content

fix: add explicit target mode fallback for non-standard resolutions (#594)#595

Open
qiin2333 wants to merge 2 commits intomasterfrom
fix/explicit-target-mode-fallback
Open

fix: add explicit target mode fallback for non-standard resolutions (#594)#595
qiin2333 wants to merge 2 commits intomasterfrom
fix/explicit-target-mode-fallback

Conversation

@qiin2333
Copy link
Copy Markdown
Collaborator

Problem

When streaming to devices with non-standard resolutions like 2560x1600 (16:10 aspect ratio), the SetDisplayConfig API returns ERROR_GEN_FAILURE because 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() in device_modes.cpp:

  1. First attempt (SDC_ALLOW_CHANGES): Windows adjusts the mode to something else (e.g., 2560x1440), fuzzy compare detects mismatch
  2. Second attempt (strict mode): SetDisplayConfig returns ERROR_GEN_FAILURE because the CCD database has no mapping for the non-standard resolution

Standard resolutions like 2560x1440 work because Windows has built-in mode matching support for common 16:9 modes.

Root Cause

The current code clears the targetModeInfoIdx and desktopModeInfoIdx in 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 new do_set_modes_with_explicit_target() function that:

  • Does NOT clear the target mode index
  • Instead, directly modifies the existing target mode entry in-place with the desired resolution and signal parameters (activeSize, totalSize, vSyncFreq, hSyncFreq, pixelRate)
  • Also updates the desktop image info if present

This bypasses Windows CCD's automatic mode matching entirely by providing a complete, self-consistent display configuration.

Impact

  • Zero impact on existing working environments: The new fallback only triggers when both original attempts fail
  • Fixes non-standard resolution support: Resolutions like 2560x1600, 1920x1200, etc. on virtual displays
  • Single file change: Only device_modes.cpp is modified

Closes #594

…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
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 13, 2026

Summary by CodeRabbit

  • Bug 修复
    • 提升了 Windows 下显示配置应用的稳健性,优化多显示器场景中显示模式的匹配与应用流程
    • 增加更可靠的回退与重试逻辑,在首次应用失败时尝试额外策略以提高配置成功率
    • 改善失败检测,减少配置回滚和错误发生的概率,提升用户显示设置体验

Walkthrough

在 Windows CCD 显示配置路径中新增内部回退助手 do_set_modes_with_explicit_target(...):在初始两次尝试(推荐策略与更严格策略)均未使所有请求模式生效后,重查询当前拓扑、在不清除 targetModeInfoIdx 的情况下原地更新目标模式的 targetVideoSignalInfo,并通过 SetDisplayConfig(...) 带上 SDC_USE_SUPPLIED_DISPLAY_CONFIG 等标志应用修改;若随后验证模式匹配则返回成功,否则继续现有回滚/失败路径。

Changes

Cohort / File(s) Summary
Windows 显示模式设置与回退
src/platform/windows/display_device/device_modes.cpp
新增 do_set_modes_with_explicit_target(...),该函数重查询活动显示配置、更新受影响路径的源模式分辨率和目标模式的视频信号信息(主动/总像素尺寸、vSync/hSync 频率、像素率、扫描类型),然后使用 SetDisplayConfig()(带 `SDC_APPLY

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding an explicit target mode fallback to handle non-standard resolutions, directly addressing issue #594.
Description check ✅ Passed The description clearly explains the problem (non-standard resolution support), root cause (automatic mode matching failure), solution (explicit target mode modification), and impact, directly relating to the changeset.
Linked Issues check ✅ Passed The PR implements key coding requirements from #594: adds fallback logic for non-standard resolutions (2560×1600), modifies target mode in-place, preserves existing paths, and includes appropriate logging.
Out of Scope Changes check ✅ Passed All changes are confined to device_modes.cpp and directly address the linked issue #594 regarding SetDisplayConfig failures for non-standard resolutions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/explicit-target-mode-fallback

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7f11979 and 2d4dd93.

📒 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: 桌面图像信息更新逻辑正确

正确地只更新了 rightbottom 字段(lefttop 保持为 0),这符合 Windows RECT 结构的原点坐标惯例。


443-453: 控制流变更正确,遵循现有模式

新增的回退逻辑正确地遵循了函数中已建立的模式:

  1. 仅在推荐模式和严格模式都失败后才尝试
  2. 在尝试前记录信息日志
  3. 成功后验证模式是否匹配
  4. 失败时继续执行原有的恢复逻辑

这种渐进式回退策略确保了现有工作场景不受影响。


299-301: 日志级别使用合理

这里使用 warning 而非 error 级别是合理的,因为这是回退路径,失败后还会尝试恢复原始模式。这与主路径 do_set_modes 中使用 error 级别形成了适当的区分。


262-271: 这些信号参数计算是 Windows 虚拟显示驱动 (VDD) 的标准做法,与 Microsoft 官方 IddCx 示例代码一致,无需进一步验证。设置 activeSizetotalSize 相同(无消隐间隔),以及使用 hSyncFreq = vSync * HeightpixelRate = vSync * Width * Height / Denominator 的公式,都是 VDD 实现的规范方式。

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2d4dd93 and 393174c.

📒 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 处理。

Comment on lines +429 to +438
// 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;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

这个回退链在 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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 2560x1600x144 分辨率设置失败 (ERROR_GEN_FAILURE) - Windows CCD 拓扑数据库兼容问题

1 participant