Merged
Conversation
Contributor
There was a problem hiding this comment.
Hey - 我发现了两个问题,并给出了一些整体性的反馈:
- 记录
ScreencapMethod日志(例如LogInfo << "Testing" << method和VAR(method))时,需要为该枚举提供operator<<重载,或者显式转换为整型;否则这些日志调用可能无法编译,或者会产生难以阅读的输出。 - 当
screencap_method_ == MaaWin32ScreencapMethod_None时,init_screencap()现在会返回true并让screencap_保持为空指针,这改变了之前“缺少 method 视为错误”的行为;如果调用方在connect()之后假定screencap_一定是有效的,建议要么保持原来的失败语义,要么显式文档化并在代码中防范nullptr情况。 - 当前的
speed_test对每种 screencap method 只调用一次screencap()来测量;为了降低方差(例如首次调用的初始化开销)并提高选择结果的稳定性,建议对每种 method 运行多次并忽略明显的离群值,或者增加一个短暂的预热阶段。
给 AI Agent 的提示
Please address the comments from this code review:
## Overall Comments
- Logging `ScreencapMethod` (e.g., `LogInfo << "Testing" << method` and `VAR(method)`) will require an `operator<<` overload for the enum or an explicit cast to an integral type; otherwise these log calls may not compile or will produce unreadable output.
- When `screencap_method_ == MaaWin32ScreencapMethod_None`, `init_screencap()` now returns `true` and leaves `screencap_` null, changing the previous behavior (which treated missing method as error); if callers assume a valid `screencap_` after `connect()`, consider either keeping the failure semantics or explicitly documenting and guarding against the `nullptr` case.
- The `speed_test` currently measures each screencap method with a single `screencap()` call; to reduce variance (e.g., first-call initialization costs) and improve selection stability, consider running each method multiple times and ignoring obvious outliers or adding a short warm-up phase.
## Individual Comments
### Comment 1
<location path="source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp" line_range="171-172" />
<code_context>
+ }
+}
+
+std::shared_ptr<ScreencapBase> Win32ControlUnitMgr::speed_test(
+ std::unordered_map<ScreencapMethod, std::shared_ptr<ScreencapBase>>& units) const
+{
+ LogFunc;
</code_context>
<issue_to_address>
**suggestion:** Use a const reference for the `units` parameter in `speed_test` to better express intent.
Since `speed_test` doesn’t modify `units`, the parameter can be `const std::unordered_map<ScreencapMethod, std::shared_ptr<ScreencapBase>>&`. This improves const-correctness and allows calling it with const maps.
```suggestion
std::shared_ptr<ScreencapBase> Win32ControlUnitMgr::speed_test(
const std::unordered_map<ScreencapMethod, std::shared_ptr<ScreencapBase>>& units) const
```
</issue_to_address>
### Comment 2
<location path="source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp" line_range="188-195" />
<code_context>
+ LogInfo << VAR(method) << VAR(duration);
+ };
+
+ for (auto& [method, unit] : units) {
+ LogInfo << "Testing" << method;
+ auto now = std::chrono::steady_clock::now();
+ if (!unit->screencap()) {
+ LogWarn << "failed to test" << method;
+ continue;
+ }
+ check(method, now);
+ }
+
</code_context>
<issue_to_address>
**suggestion (performance):** Consider multiple samples or a warmup for each screencap method to avoid one-off timing artifacts.
Using only one timing per method means any one-time initialization (e.g., GPU/driver setup, first capture overhead) can dominate the measurement and bias the selection toward a method that’s not actually best in steady state. To reduce this, consider adding a warmup call and/or measuring min/average over several `screencap()` calls per method.
Suggested implementation:
```cpp
auto check = [&fastest, &cost](ScreencapMethod method, std::chrono::steady_clock::duration duration) {
if (duration < cost) {
fastest = method;
cost = duration;
}
LogInfo << VAR(method) << VAR(duration);
};
```
```cpp
constexpr int warmup_runs = 1;
constexpr int measure_runs = 5;
for (auto& [method, unit] : units) {
LogInfo << "Testing" << method;
bool warmup_ok = true;
for (int i = 0; i < warmup_runs; ++i) {
if (!unit->screencap()) {
LogWarn << "failed to warmup" << method;
warmup_ok = false;
break;
}
}
if (!warmup_ok) {
continue;
}
std::chrono::steady_clock::duration best_duration = std::chrono::steady_clock::duration::max();
bool any_success = false;
for (int i = 0; i < measure_runs; ++i) {
auto start = std::chrono::steady_clock::now();
if (!unit->screencap()) {
LogWarn << "failed to test" << method;
continue;
}
auto duration = duration_since(start);
any_success = true;
if (duration < best_duration) {
best_duration = duration;
}
}
if (!any_success) {
continue;
}
check(method, best_duration);
}
```
</issue_to_address>帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- Logging
ScreencapMethod(e.g.,LogInfo << "Testing" << methodandVAR(method)) will require anoperator<<overload for the enum or an explicit cast to an integral type; otherwise these log calls may not compile or will produce unreadable output. - When
screencap_method_ == MaaWin32ScreencapMethod_None,init_screencap()now returnstrueand leavesscreencap_null, changing the previous behavior (which treated missing method as error); if callers assume a validscreencap_afterconnect(), consider either keeping the failure semantics or explicitly documenting and guarding against thenullptrcase. - The
speed_testcurrently measures each screencap method with a singlescreencap()call; to reduce variance (e.g., first-call initialization costs) and improve selection stability, consider running each method multiple times and ignoring obvious outliers or adding a short warm-up phase.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Logging `ScreencapMethod` (e.g., `LogInfo << "Testing" << method` and `VAR(method)`) will require an `operator<<` overload for the enum or an explicit cast to an integral type; otherwise these log calls may not compile or will produce unreadable output.
- When `screencap_method_ == MaaWin32ScreencapMethod_None`, `init_screencap()` now returns `true` and leaves `screencap_` null, changing the previous behavior (which treated missing method as error); if callers assume a valid `screencap_` after `connect()`, consider either keeping the failure semantics or explicitly documenting and guarding against the `nullptr` case.
- The `speed_test` currently measures each screencap method with a single `screencap()` call; to reduce variance (e.g., first-call initialization costs) and improve selection stability, consider running each method multiple times and ignoring obvious outliers or adding a short warm-up phase.
## Individual Comments
### Comment 1
<location path="source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp" line_range="171-172" />
<code_context>
+ }
+}
+
+std::shared_ptr<ScreencapBase> Win32ControlUnitMgr::speed_test(
+ std::unordered_map<ScreencapMethod, std::shared_ptr<ScreencapBase>>& units) const
+{
+ LogFunc;
</code_context>
<issue_to_address>
**suggestion:** Use a const reference for the `units` parameter in `speed_test` to better express intent.
Since `speed_test` doesn’t modify `units`, the parameter can be `const std::unordered_map<ScreencapMethod, std::shared_ptr<ScreencapBase>>&`. This improves const-correctness and allows calling it with const maps.
```suggestion
std::shared_ptr<ScreencapBase> Win32ControlUnitMgr::speed_test(
const std::unordered_map<ScreencapMethod, std::shared_ptr<ScreencapBase>>& units) const
```
</issue_to_address>
### Comment 2
<location path="source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp" line_range="188-195" />
<code_context>
+ LogInfo << VAR(method) << VAR(duration);
+ };
+
+ for (auto& [method, unit] : units) {
+ LogInfo << "Testing" << method;
+ auto now = std::chrono::steady_clock::now();
+ if (!unit->screencap()) {
+ LogWarn << "failed to test" << method;
+ continue;
+ }
+ check(method, now);
+ }
+
</code_context>
<issue_to_address>
**suggestion (performance):** Consider multiple samples or a warmup for each screencap method to avoid one-off timing artifacts.
Using only one timing per method means any one-time initialization (e.g., GPU/driver setup, first capture overhead) can dominate the measurement and bias the selection toward a method that’s not actually best in steady state. To reduce this, consider adding a warmup call and/or measuring min/average over several `screencap()` calls per method.
Suggested implementation:
```cpp
auto check = [&fastest, &cost](ScreencapMethod method, std::chrono::steady_clock::duration duration) {
if (duration < cost) {
fastest = method;
cost = duration;
}
LogInfo << VAR(method) << VAR(duration);
};
```
```cpp
constexpr int warmup_runs = 1;
constexpr int measure_runs = 5;
for (auto& [method, unit] : units) {
LogInfo << "Testing" << method;
bool warmup_ok = true;
for (int i = 0; i < warmup_runs; ++i) {
if (!unit->screencap()) {
LogWarn << "failed to warmup" << method;
warmup_ok = false;
break;
}
}
if (!warmup_ok) {
continue;
}
std::chrono::steady_clock::duration best_duration = std::chrono::steady_clock::duration::max();
bool any_success = false;
for (int i = 0; i < measure_runs; ++i) {
auto start = std::chrono::steady_clock::now();
if (!unit->screencap()) {
LogWarn << "failed to test" << method;
continue;
}
auto duration = duration_since(start);
any_success = true;
if (duration < best_duration) {
best_duration = duration;
}
}
if (!any_success) {
continue;
}
check(method, best_duration);
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Win32 controller to treat screencap methods as bitmask flags and introduces a startup speed test to automatically select the fastest working method among the provided options, with corresponding updates to C/C++ exports, Python/NodeJS bindings, and English documentation.
Changes:
- Add Win32 screencap method flag combinations (
All,Foreground,Background) across C API, C++ module exports, and language bindings. - Refactor Win32 controller connection to build candidate screencap units and pick the fastest via runtime testing.
- Update NodeJS/Python docs/types and Win32 screencap documentation to describe the new flag-based selection model.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| source/modules/MaaFramework.cppm | Exports new Win32 screencap method flag constants to the module interface. |
| include/MaaFramework/MaaDef.h | Defines new Win32 screencap combination macros and updates API doc comments to reflect flag semantics. |
| source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.h | Adds internal screencap selection helpers and data structures to support speed testing. |
| source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp | Implements flag-based unit construction and a runtime speed test to select the fastest screencap method. |
| source/binding/Python/maa/define.py | Updates Win32 screencap enum to flags and adds All/Foreground/Background. |
| source/binding/Python/maa/controller.py | Changes Win32Controller default screencap selection to Background. |
| source/binding/NodeJS/src/apis/constant.d.ts | Updates Win32 screencap method documentation and adds new keys to the typed constant map. |
| source/binding/NodeJS/src/apis/constant.cpp | Exposes new Win32 screencap flag constants to JS. |
| docs/en_us/2.4-ControlMethods.md | Updates Win32 screencap documentation to describe OR-combination and predefined combos. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary by Sourcery
为 Win32 屏幕捕获方法添加支持,使其可作为按位标志组合使用,并在运行时自动选择可用的最快方法。
新功能:
Win32ControlUnitMgr中引入运行时基准测试,用于测试已配置的屏幕捕获方法,并为目标窗口选择最快且可用的实现。增强:
Win32ControlUnitMgr,以封装屏幕捕获初始化、复用共享的输入创建逻辑,并确保在重连时重建屏幕捕获对象。文档:
Original summary in English
Summary by Sourcery
Add support for combining multiple Win32 screencap methods as bitwise flags and automatically selecting the fastest available method at runtime.
New Features:
Enhancements:
Documentation: