feat(display): add display mode remapping option (#3529)
Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
This commit is contained in:
parent
012a99c26d
commit
1b94e9339a
11 changed files with 701 additions and 35 deletions
|
|
@ -1234,6 +1234,92 @@ editing the `conf` file in a text editor. Use the examples as reference.
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
### dd_mode_remapping
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td colspan="2">
|
||||
Remap the requested resolution and FPS to another display mode.<br>
|
||||
Depending on the [dd_resolution_option](#dd_resolution_option) and
|
||||
[dd_refresh_rate_option](#dd_refresh_rate_option) values, the following mapping
|
||||
groups are available:
|
||||
<ul>
|
||||
<li>`mixed` - both options are set to `auto`.</li>
|
||||
<li>
|
||||
`resolution_only` - only [dd_resolution_option](#dd_resolution_option) is set to `auto`.
|
||||
</li>
|
||||
<li>
|
||||
`refresh_rate_only` - only [dd_refresh_rate_option](#dd_refresh_rate_option) is set to `auto`.
|
||||
</li>
|
||||
</ul>
|
||||
For each of those groups, a list of fields can be configured to perform remapping:
|
||||
<ul>
|
||||
<li>
|
||||
`requested_resolution` - resolution that needs to be matched in order to use this remapping entry.
|
||||
</li>
|
||||
<li>`requested_fps` - FPS that needs to be matched in order to use this remapping entry.</li>
|
||||
<li>`final_resolution` - resolution value to be used if the entry was matched.</li>
|
||||
<li>`final_refresh_rate` - refresh rate value to be used if the entry was matched.</li>
|
||||
</ul>
|
||||
If `requested_*` field is left empty, it will match <b>everything</b>.<br>
|
||||
If `final_*` field is left empty, the original value will not be remapped and either a requested, manual
|
||||
or current value is used. However, at least one `final_*` must be set, otherwise the entry is considered
|
||||
invalid.<br>
|
||||
@note{"Optimize game settings" must be enabled on client side for ANY entry with `resolution`
|
||||
field to be considered.}
|
||||
@note{First entry to be matched in the list is the one that will be used.}
|
||||
@tip{`requested_resolution` and `final_resolution` can be omitted for `refresh_rate_only` group.}
|
||||
@tip{`requested_fps` and `final_refresh_rate` can be omitted for `resolution_only` group.}
|
||||
@note{Applies to Windows only.}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Default</td>
|
||||
<td colspan="2">@code{}
|
||||
dd_mode_remapping = {
|
||||
"mixed": [],
|
||||
"resolution_only": [],
|
||||
"refresh_rate_only": []
|
||||
}
|
||||
@endcode
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example</td>
|
||||
<td colspan="2">@code{}
|
||||
dd_mode_remapping = {
|
||||
"mixed": [
|
||||
{
|
||||
"requested_fps": "60",
|
||||
"final_refresh_rate": "119.95",
|
||||
"requested_resolution": "1920x1080",
|
||||
"final_resolution": "2560x1440"
|
||||
},
|
||||
{
|
||||
"requested_fps": "60",
|
||||
"final_refresh_rate": "120",
|
||||
"requested_resolution": "",
|
||||
"final_resolution": ""
|
||||
}
|
||||
],
|
||||
"resolution_only": [
|
||||
{
|
||||
"requested_resolution": "1920x1080",
|
||||
"final_resolution": "2560x1440"
|
||||
}
|
||||
],
|
||||
"refresh_rate_only": [
|
||||
{
|
||||
"requested_fps": "60",
|
||||
"final_refresh_rate": "119.95"
|
||||
}
|
||||
]
|
||||
}@endcode
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### min_fps_factor
|
||||
|
||||
<table>
|
||||
|
|
|
|||
|
|
@ -379,6 +379,38 @@ namespace config {
|
|||
#undef _CONVERT_2_ARG_
|
||||
return video_t::dd_t::hdr_option_e::disabled; // Default to this if value is invalid
|
||||
}
|
||||
|
||||
video_t::dd_t::mode_remapping_t
|
||||
mode_remapping_from_view(const std::string_view value) {
|
||||
const auto parse_entry_list { [](const auto &entry_list, auto &output_field) {
|
||||
for (auto &[_, entry] : entry_list) {
|
||||
auto requested_resolution = entry.template get_optional<std::string>("requested_resolution"s);
|
||||
auto requested_fps = entry.template get_optional<std::string>("requested_fps"s);
|
||||
auto final_resolution = entry.template get_optional<std::string>("final_resolution"s);
|
||||
auto final_refresh_rate = entry.template get_optional<std::string>("final_refresh_rate"s);
|
||||
|
||||
output_field.push_back(video_t::dd_t::mode_remapping_entry_t {
|
||||
requested_resolution.value_or(""),
|
||||
requested_fps.value_or(""),
|
||||
final_resolution.value_or(""),
|
||||
final_refresh_rate.value_or("") });
|
||||
}
|
||||
} };
|
||||
|
||||
// We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it.
|
||||
std::stringstream json_stream;
|
||||
json_stream << "{\"dd_mode_remapping\":" << value << "}";
|
||||
|
||||
boost::property_tree::ptree json_tree;
|
||||
boost::property_tree::read_json(json_stream, json_tree);
|
||||
|
||||
video_t::dd_t::mode_remapping_t output;
|
||||
parse_entry_list(json_tree.get_child("dd_mode_remapping.mixed"), output.mixed);
|
||||
parse_entry_list(json_tree.get_child("dd_mode_remapping.resolution_only"), output.resolution_only);
|
||||
parse_entry_list(json_tree.get_child("dd_mode_remapping.refresh_rate_only"), output.refresh_rate_only);
|
||||
|
||||
return output;
|
||||
}
|
||||
} // namespace dd
|
||||
|
||||
video_t video {
|
||||
|
|
@ -447,6 +479,7 @@ namespace config {
|
|||
{}, // manual_refresh_rate
|
||||
video_t::dd_t::hdr_option_e::automatic, // hdr_option
|
||||
3s, // config_revert_delay
|
||||
{}, // mode_remapping
|
||||
{} // wa
|
||||
}, // display_device
|
||||
|
||||
|
|
@ -1105,6 +1138,7 @@ namespace config {
|
|||
video.dd.config_revert_delay = std::chrono::milliseconds { value };
|
||||
}
|
||||
}
|
||||
generic_f(vars, "dd_mode_remapping", video.dd.mode_remapping, dd::mode_remapping_from_view);
|
||||
bool_f(vars, "dd_wa_hdr_toggle", video.dd.wa.hdr_toggle);
|
||||
|
||||
int_between_f(vars, "min_fps_factor", video.min_fps_factor, { 1, 3 });
|
||||
|
|
|
|||
14
src/config.h
14
src/config.h
|
|
@ -110,6 +110,19 @@ namespace config {
|
|||
automatic ///< Change HDR settings and use the state requested by Moonlight.
|
||||
};
|
||||
|
||||
struct mode_remapping_entry_t {
|
||||
std::string requested_resolution;
|
||||
std::string requested_fps;
|
||||
std::string final_resolution;
|
||||
std::string final_refresh_rate;
|
||||
};
|
||||
|
||||
struct mode_remapping_t {
|
||||
std::vector<mode_remapping_entry_t> mixed; ///< To be used when `resolution_option` and `refresh_rate_option` is set to `automatic`.
|
||||
std::vector<mode_remapping_entry_t> resolution_only; ///< To be use when only `resolution_option` is set to `automatic`.
|
||||
std::vector<mode_remapping_entry_t> refresh_rate_only; ///< To be use when only `refresh_rate_option` is set to `automatic`.
|
||||
};
|
||||
|
||||
config_option_e configuration_option;
|
||||
resolution_option_e resolution_option;
|
||||
std::string manual_resolution; ///< Manual resolution in case `resolution_option == resolution_option_e::manual`.
|
||||
|
|
@ -117,6 +130,7 @@ namespace config {
|
|||
std::string manual_refresh_rate; ///< Manual refresh rate in case `refresh_rate_option == refresh_rate_option_e::manual`.
|
||||
hdr_option_e hdr_option;
|
||||
std::chrono::milliseconds config_revert_delay; ///< Time to wait until settings are reverted (after stream ends/app exists).
|
||||
mode_remapping_t mode_remapping;
|
||||
workarounds_t wa;
|
||||
} dd;
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ namespace display_device {
|
|||
* @brief Parse refresh rate value from the string.
|
||||
* @param input String to be parsed.
|
||||
* @param output Reference to output variable to fill in.
|
||||
* @param allow_decimal_point Specify whether the decimal point is allowed or not.
|
||||
* @returns True on successful parsing (empty string allowed), false otherwise.
|
||||
*
|
||||
* @examples
|
||||
|
|
@ -203,10 +204,10 @@ namespace display_device {
|
|||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
parse_refresh_rate_string(const std::string &input, std::optional<FloatingPoint> &output) {
|
||||
parse_refresh_rate_string(const std::string &input, std::optional<FloatingPoint> &output, const bool allow_decimal_point = true) {
|
||||
static const auto is_zero { [](const auto &character) { return character == '0'; } };
|
||||
const std::string trimmed_input { boost::algorithm::trim_copy(input) };
|
||||
const std::regex refresh_rate_regex { R"(^(\d+)(?:\.(\d+))?$)" };
|
||||
const std::regex refresh_rate_regex { allow_decimal_point ? R"(^(\d+)(?:\.(\d+))?$)" : R"(^(\d+)$)" };
|
||||
|
||||
if (std::smatch match; std::regex_match(trimmed_input, match, refresh_rate_regex)) {
|
||||
try {
|
||||
|
|
@ -217,7 +218,7 @@ namespace display_device {
|
|||
}
|
||||
|
||||
std::string trimmed_match_2;
|
||||
if (match[2].matched) {
|
||||
if (allow_decimal_point && match[2].matched) {
|
||||
trimmed_match_2 = boost::algorithm::trim_right_copy_if(match[2].str(), is_zero);
|
||||
}
|
||||
|
||||
|
|
@ -261,7 +262,7 @@ namespace display_device {
|
|||
return true;
|
||||
}
|
||||
|
||||
BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << R"(. Must have a pattern of "123" or "123.456"!)";
|
||||
BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << ". Must have a pattern of " << (allow_decimal_point ? R"("123" or "123.456")" : R"("123")") << "!";
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -435,6 +436,196 @@ namespace display_device {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Indicates which remapping fields and config structure shall be used.
|
||||
*/
|
||||
enum class remapping_type_e {
|
||||
mixed, ///! Both reseolution and refresh rate may be remapped
|
||||
resolution_only, ///! Only resolution will be remapped
|
||||
refresh_rate_only ///! Only refresh rate will be remapped
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Determine the ramapping type from the user config.
|
||||
* @param video_config User's video related configuration.
|
||||
* @returns Enum value if remapping can be performed, null optional if remapping shall be skipped.
|
||||
*/
|
||||
std::optional<remapping_type_e>
|
||||
determine_remapping_type(const config::video_t &video_config) {
|
||||
using dd_t = config::video_t::dd_t;
|
||||
const bool auto_resolution { video_config.dd.resolution_option == dd_t::resolution_option_e::automatic };
|
||||
const bool auto_refresh_rate { video_config.dd.refresh_rate_option == dd_t::refresh_rate_option_e::automatic };
|
||||
|
||||
if (auto_resolution && auto_refresh_rate) {
|
||||
return remapping_type_e::mixed;
|
||||
}
|
||||
|
||||
if (auto_resolution) {
|
||||
return remapping_type_e::resolution_only;
|
||||
}
|
||||
|
||||
if (auto_refresh_rate) {
|
||||
return remapping_type_e::refresh_rate_only;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Contains remapping data parsed from the string values.
|
||||
*/
|
||||
struct parsed_remapping_entry_t {
|
||||
std::optional<Resolution> requested_resolution;
|
||||
std::optional<FloatingPoint> requested_fps;
|
||||
std::optional<Resolution> final_resolution;
|
||||
std::optional<FloatingPoint> final_refresh_rate;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Check if resolution is to be mapped based on remmaping type.
|
||||
* @param type Remapping type to check.
|
||||
* @returns True if resolution is to be mapped, false otherwise.
|
||||
*/
|
||||
bool
|
||||
is_resolution_mapped(const remapping_type_e type) {
|
||||
return type == remapping_type_e::resolution_only || type == remapping_type_e::mixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if FPS is to be mapped based on remmaping type.
|
||||
* @param type Remapping type to check.
|
||||
* @returns True if FPS is to be mapped, false otherwise.
|
||||
*/
|
||||
bool
|
||||
is_fps_mapped(const remapping_type_e type) {
|
||||
return type == remapping_type_e::refresh_rate_only || type == remapping_type_e::mixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse the remapping entry from the config into an internal structure.
|
||||
* @param entry Entry to parse.
|
||||
* @param type Specify which entry fields should be parsed.
|
||||
* @returns Parsed structure or null optional if a necessary field could not be parsed.
|
||||
*/
|
||||
std::optional<parsed_remapping_entry_t>
|
||||
parse_remapping_entry(const config::video_t::dd_t::mode_remapping_entry_t &entry, const remapping_type_e type) {
|
||||
parsed_remapping_entry_t result {};
|
||||
|
||||
if (is_resolution_mapped(type) && (!parse_resolution_string(entry.requested_resolution, result.requested_resolution) ||
|
||||
!parse_resolution_string(entry.final_resolution, result.final_resolution))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (is_fps_mapped(type) && (!parse_refresh_rate_string(entry.requested_fps, result.requested_fps, false) ||
|
||||
!parse_refresh_rate_string(entry.final_refresh_rate, result.final_refresh_rate))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remap the the requested display mode based on the config.
|
||||
* @param video_config User's video related configuration.
|
||||
* @param session Session information.
|
||||
* @param config A reference to a config object that will be modified on success.
|
||||
* @returns True if the remapping was performed or skipped, false if remapping has failed due to invalid config.
|
||||
*
|
||||
* @examples
|
||||
* const std::shared_ptr<rtsp_stream::launch_session_t> launch_session;
|
||||
* const config::video_t &video_config { config::video };
|
||||
*
|
||||
* SingleDisplayConfiguration config;
|
||||
* const bool success = remap_display_mode_if_needed(video_config, *launch_session, config);
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
remap_display_mode_if_needed(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) {
|
||||
const auto remapping_type { determine_remapping_type(video_config) };
|
||||
if (!remapping_type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &remapping_list { [&]() {
|
||||
using enum remapping_type_e;
|
||||
|
||||
switch (*remapping_type) {
|
||||
case resolution_only:
|
||||
return video_config.dd.mode_remapping.resolution_only;
|
||||
case refresh_rate_only:
|
||||
return video_config.dd.mode_remapping.refresh_rate_only;
|
||||
case mixed:
|
||||
default:
|
||||
return video_config.dd.mode_remapping.mixed;
|
||||
}
|
||||
}() };
|
||||
|
||||
if (remapping_list.empty()) {
|
||||
BOOST_LOG(debug) << "No values are available for display mode remapping.";
|
||||
return true;
|
||||
}
|
||||
BOOST_LOG(debug) << "Trying to remap display modes...";
|
||||
|
||||
const auto entry_to_string { [type = *remapping_type](const config::video_t::dd_t::mode_remapping_entry_t &entry) {
|
||||
const bool mapping_resolution { is_resolution_mapped(type) };
|
||||
const bool mapping_fps { is_fps_mapped(type) };
|
||||
|
||||
// clang-format off
|
||||
return (mapping_resolution ? " - requested resolution: "s + entry.requested_resolution + "\n" : "") +
|
||||
(mapping_fps ? " - requested FPS: "s + entry.requested_fps + "\n" : "") +
|
||||
(mapping_resolution ? " - final resolution: "s + entry.final_resolution + "\n" : "") +
|
||||
(mapping_fps ? " - final refresh rate: "s + entry.final_refresh_rate : "");
|
||||
// clang-format on
|
||||
} };
|
||||
|
||||
for (const auto &entry : remapping_list) {
|
||||
const auto parsed_entry { parse_remapping_entry(entry, *remapping_type) };
|
||||
if (!parsed_entry) {
|
||||
BOOST_LOG(error) << "Failed to parse remapping entry from:\n"
|
||||
<< entry_to_string(entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parsed_entry->final_resolution && !parsed_entry->final_refresh_rate) {
|
||||
BOOST_LOG(error) << "At least one final value must be set for remapping display modes! Entry:\n"
|
||||
<< entry_to_string(entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!session.enable_sops && (parsed_entry->requested_resolution || parsed_entry->final_resolution)) {
|
||||
BOOST_LOG(warning) << R"(Skipping remapping entry, because the "Optimize game settings" is not set in the client! Entry:\n)"
|
||||
<< entry_to_string(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: at this point config should already have parsed resolution set.
|
||||
if (parsed_entry->requested_resolution && parsed_entry->requested_resolution != config.m_resolution) {
|
||||
BOOST_LOG(verbose) << "Skipping remapping because requested resolutions do not match! Entry:\n"
|
||||
<< entry_to_string(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: at this point config should already have parsed refresh rate set.
|
||||
if (parsed_entry->requested_fps && parsed_entry->requested_fps != config.m_refresh_rate) {
|
||||
BOOST_LOG(verbose) << "Skipping remapping because requested FPS do not match! Entry:\n"
|
||||
<< entry_to_string(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "Remapping requested display mode. Entry:\n"
|
||||
<< entry_to_string(entry);
|
||||
if (parsed_entry->final_resolution) {
|
||||
config.m_resolution = parsed_entry->final_resolution;
|
||||
}
|
||||
if (parsed_entry->final_refresh_rate) {
|
||||
config.m_refresh_rate = parsed_entry->final_refresh_rate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a settings manager interface to manage display device settings.
|
||||
* @param persistence_filepath File location for saving persistent state.
|
||||
|
|
@ -626,6 +817,11 @@ namespace display_device {
|
|||
return failed_to_parse_tag_t {};
|
||||
}
|
||||
|
||||
if (!remap_display_mode_if_needed(video_config, session, config)) {
|
||||
// Error already logged
|
||||
return failed_to_parse_tag_t {};
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
} // namespace display_device
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
<general
|
||||
v-if="currentTab === 'general'"
|
||||
:config="config"
|
||||
:global-prep-cmd="global_prep_cmd"
|
||||
:platform="platform">
|
||||
</general>
|
||||
|
||||
|
|
@ -127,7 +126,6 @@
|
|||
restarted: false,
|
||||
config: null,
|
||||
currentTab: "general",
|
||||
global_prep_cmd: [],
|
||||
tabs: [ // TODO: Move the options to each Component instead, encapsulate.
|
||||
{
|
||||
id: "general",
|
||||
|
|
@ -136,7 +134,7 @@
|
|||
"locale": "en",
|
||||
"sunshine_name": "",
|
||||
"min_log_level": 2,
|
||||
"global_prep_cmd": "[]",
|
||||
"global_prep_cmd": [],
|
||||
"notify_pre_releases": "disabled",
|
||||
},
|
||||
},
|
||||
|
|
@ -178,6 +176,7 @@
|
|||
"dd_manual_refresh_rate": "",
|
||||
"dd_hdr_option": "auto",
|
||||
"dd_config_revert_delay": 3000,
|
||||
"dd_mode_remapping": {"mixed": [], "resolution_only": [], "refresh_rate_only": []},
|
||||
"dd_wa_hdr_toggle": "disabled",
|
||||
"min_fps_factor": 1,
|
||||
},
|
||||
|
|
@ -320,17 +319,23 @@
|
|||
|
||||
// TODO: let each tab's Component handle it's own data instead of doing it here
|
||||
|
||||
// Parse the special options before population if available
|
||||
const specialOptions = ["dd_mode_remapping", "global_prep_cmd"]
|
||||
for (const optionKey of specialOptions) {
|
||||
if (this.config.hasOwnProperty(optionKey)) {
|
||||
this.config[optionKey] = JSON.parse(this.config[optionKey]);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate default values from tabs options
|
||||
this.tabs.forEach(tab => {
|
||||
Object.keys(tab.options).forEach(optionKey => {
|
||||
if (this.config[optionKey] === undefined) {
|
||||
this.config[optionKey] = tab.options[optionKey];
|
||||
// Make sure to copy by value
|
||||
this.config[optionKey] = JSON.parse(JSON.stringify(tab.options[optionKey]));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.config.global_prep_cmd = this.config.global_prep_cmd || [];
|
||||
this.global_prep_cmd = JSON.parse(this.config.global_prep_cmd);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -338,26 +343,26 @@
|
|||
this.$forceUpdate()
|
||||
},
|
||||
serialize() {
|
||||
this.config.global_prep_cmd = JSON.stringify(this.global_prep_cmd);
|
||||
let config = JSON.parse(JSON.stringify(this.config));
|
||||
config.global_prep_cmd = JSON.stringify(config.global_prep_cmd);
|
||||
config.dd_mode_remapping = JSON.stringify(config.dd_mode_remapping);
|
||||
return config;
|
||||
},
|
||||
save() {
|
||||
this.saved = false;
|
||||
this.restarted = false;
|
||||
this.serialize();
|
||||
|
||||
// create a temp copy of this.config to use for the post request
|
||||
let config = JSON.parse(JSON.stringify(this.config))
|
||||
let config = this.serialize();
|
||||
|
||||
// delete default values from this.config
|
||||
this.tabs.forEach(tab => {
|
||||
Object.keys(tab.options).forEach(optionKey => {
|
||||
let delete_value = false
|
||||
|
||||
if (["global_prep_cmd"].includes(optionKey)) {
|
||||
let config_value, default_value
|
||||
|
||||
config_value = JSON.parse(config[optionKey])
|
||||
default_value = JSON.parse(tab.options[optionKey])
|
||||
if (["global_prep_cmd", "dd_mode_remapping"].includes(optionKey)) {
|
||||
const config_value = config[optionKey]
|
||||
const default_value = JSON.stringify(tab.options[optionKey])
|
||||
|
||||
if (config_value === default_value) {
|
||||
delete_value = true
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import Checkbox from "../../Checkbox.vue";
|
|||
const props = defineProps([
|
||||
'platform',
|
||||
'config',
|
||||
'min_fps_factor',
|
||||
])
|
||||
|
||||
const config = ref(props.config)
|
||||
|
|
@ -95,7 +94,6 @@ const config = ref(props.config)
|
|||
<DisplayModesSettings
|
||||
:platform="platform"
|
||||
:config="config"
|
||||
:min_fps_factor="min_fps_factor"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,12 +4,9 @@ import { ref } from 'vue'
|
|||
|
||||
const props = defineProps({
|
||||
platform: String,
|
||||
config: Object,
|
||||
globalPrepCmd: Array
|
||||
config: Object
|
||||
})
|
||||
|
||||
const config = ref(props.config)
|
||||
const globalPrepCmd = ref(props.globalPrepCmd)
|
||||
|
||||
function addCmd() {
|
||||
let template = {
|
||||
|
|
@ -20,11 +17,11 @@ function addCmd() {
|
|||
if (props.platform === 'windows') {
|
||||
template = { ...template, elevated: false };
|
||||
}
|
||||
globalPrepCmd.value.push(template);
|
||||
config.value.global_prep_cmd.push(template);
|
||||
}
|
||||
|
||||
function removeCmd(index) {
|
||||
globalPrepCmd.value.splice(index,1)
|
||||
config.value.global_prep_cmd.splice(index,1)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -83,7 +80,7 @@ function removeCmd(index) {
|
|||
<div id="global_prep_cmd" class="mb-3 d-flex flex-column">
|
||||
<label class="form-label">{{ $t('config.global_prep_cmd') }}</label>
|
||||
<div class="form-text">{{ $t('config.global_prep_cmd_desc') }}</div>
|
||||
<table class="table" v-if="globalPrepCmd.length > 0">
|
||||
<table class="table" v-if="config.global_prep_cmd.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"><i class="fas fa-play"></i> {{ $t('_common.do_cmd') }}</th>
|
||||
|
|
@ -95,7 +92,7 @@ function removeCmd(index) {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(c, i) in globalPrepCmd">
|
||||
<tr v-for="(c, i) in config.global_prep_cmd">
|
||||
<td>
|
||||
<input type="text" class="form-control monospace" v-model="c.do" />
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,44 @@ const props = defineProps({
|
|||
platform: String,
|
||||
config: Object
|
||||
})
|
||||
|
||||
const config = ref(props.config)
|
||||
|
||||
const REFRESH_RATE_ONLY = "refresh_rate_only"
|
||||
const RESOLUTION_ONLY = "resolution_only"
|
||||
const MIXED = "mixed"
|
||||
|
||||
function canBeRemapped() {
|
||||
return (config.value.dd_resolution_option === "auto" || config.value.dd_refresh_rate_option === "auto")
|
||||
&& config.value.dd_configuration_option !== "disabled";
|
||||
}
|
||||
|
||||
function getRemappingType() {
|
||||
// Assuming here that at least one setting is set to "auto" if other is not
|
||||
if (config.value.dd_resolution_option !== "auto") {
|
||||
return REFRESH_RATE_ONLY;
|
||||
}
|
||||
if (config.value.dd_refresh_rate_option !== "auto") {
|
||||
return RESOLUTION_ONLY;
|
||||
}
|
||||
return MIXED;
|
||||
}
|
||||
|
||||
function addRemappingEntry() {
|
||||
const type = getRemappingType();
|
||||
let template = {};
|
||||
|
||||
if (type !== RESOLUTION_ONLY) {
|
||||
template["requested_fps"] = "";
|
||||
template["final_refresh_rate"] = "";
|
||||
}
|
||||
|
||||
if (type !== REFRESH_RATE_ONLY) {
|
||||
template["requested_resolution"] = "";
|
||||
template["final_resolution"] = "";
|
||||
}
|
||||
|
||||
config.value.dd_mode_remapping[type].push(template);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -114,6 +150,76 @@ const config = ref(props.config)
|
|||
{{ $t('config.dd_config_revert_delay_desc') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Display mode remapping -->
|
||||
<div class="mb-3" v-if="canBeRemapped()">
|
||||
<label for="dd_mode_remapping" class="form-label">
|
||||
{{ $t('config.dd_mode_remapping') }}
|
||||
</label>
|
||||
<div id="dd_mode_remapping" class="d-flex flex-column">
|
||||
<div class="form-text">
|
||||
{{ $t('config.dd_mode_remapping_desc_1') }}<br>
|
||||
{{ $t('config.dd_mode_remapping_desc_2') }}<br>
|
||||
{{ $t('config.dd_mode_remapping_desc_3') }}<br>
|
||||
{{ $t(getRemappingType() === MIXED ? 'config.dd_mode_remapping_desc_4_final_values_mixed' : 'config.dd_mode_remapping_desc_4_final_values_non_mixed') }}<br>
|
||||
<template v-if="getRemappingType() === MIXED">
|
||||
{{ $t('config.dd_mode_remapping_desc_5_sops_mixed_only') }}<br>
|
||||
</template>
|
||||
<template v-if="getRemappingType() === RESOLUTION_ONLY">
|
||||
{{ $t('config.dd_mode_remapping_desc_5_sops_resolution_only') }}<br>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table" v-if="config.dd_mode_remapping[getRemappingType()].length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" v-if="getRemappingType() !== REFRESH_RATE_ONLY">
|
||||
{{ $t('config.dd_mode_remapping_requested_resolution') }}
|
||||
</th>
|
||||
<th scope="col" v-if="getRemappingType() !== RESOLUTION_ONLY">
|
||||
{{ $t('config.dd_mode_remapping_requested_fps') }}
|
||||
</th>
|
||||
<th scope="col" v-if="getRemappingType() !== REFRESH_RATE_ONLY">
|
||||
{{ $t('config.dd_mode_remapping_final_resolution') }}
|
||||
</th>
|
||||
<th scope="col" v-if="getRemappingType() !== RESOLUTION_ONLY">
|
||||
{{ $t('config.dd_mode_remapping_final_refresh_rate') }}
|
||||
</th>
|
||||
<!-- Additional columns for buttons-->
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(value, idx) in config.dd_mode_remapping[getRemappingType()]">
|
||||
<td v-if="getRemappingType() !== REFRESH_RATE_ONLY">
|
||||
<input type="text" class="form-control monospace" v-model="value.requested_resolution"
|
||||
:placeholder="'1920x1080'" />
|
||||
</td>
|
||||
<td v-if="getRemappingType() !== RESOLUTION_ONLY">
|
||||
<input type="text" class="form-control monospace" v-model="value.requested_fps"
|
||||
:placeholder="'60'" />
|
||||
</td>
|
||||
<td v-if="getRemappingType() !== REFRESH_RATE_ONLY">
|
||||
<input type="text" class="form-control monospace" v-model="value.final_resolution"
|
||||
:placeholder="'2560x1440'" />
|
||||
</td>
|
||||
<td v-if="getRemappingType() !== RESOLUTION_ONLY">
|
||||
<input type="text" class="form-control monospace" v-model="value.final_refresh_rate"
|
||||
:placeholder="'119.95'" />
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" @click="config.dd_mode_remapping[getRemappingType()].splice(idx, 1)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button class="ms-0 mt-2 btn btn-success" style="margin: 0 auto" @click="addRemappingEntry()">
|
||||
+ {{ $t('config.dd_mode_remapping_add') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,13 +6,8 @@ import PlatformLayout from '../../../PlatformLayout.vue'
|
|||
const props = defineProps([
|
||||
'platform',
|
||||
'config',
|
||||
'min_fps_factor',
|
||||
])
|
||||
|
||||
const config = ref(props.config)
|
||||
|
||||
const resIn = ref("")
|
||||
const fpsIn = ref("")
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -162,6 +162,19 @@
|
|||
"dd_hdr_option": "HDR",
|
||||
"dd_hdr_option_auto": "Switch on/off the HDR mode as requested by the client (default)",
|
||||
"dd_hdr_option_disabled": "Do not change HDR settings",
|
||||
"dd_mode_remapping": "Display mode remapping",
|
||||
"dd_mode_remapping_add": "Add remapping entry",
|
||||
"dd_mode_remapping_desc_1": "Specify remapping entries to change the requested resolution and/or refresh rate to other values.",
|
||||
"dd_mode_remapping_desc_2": "The list is iterated from top to bottom and the first match is used.",
|
||||
"dd_mode_remapping_desc_3": "\"Requested\" fields can be left empty to match any requested value.",
|
||||
"dd_mode_remapping_desc_4_final_values_mixed": "At least one \"Final\" field must be specified. The unspecified resolution or refresh rate will not be changed.",
|
||||
"dd_mode_remapping_desc_4_final_values_non_mixed": "\"Final\" field must be specified and cannot be empty.",
|
||||
"dd_mode_remapping_desc_5_sops_mixed_only": "\"Optimize game settings\" option must be enabled in the Moonlight client, otherwise entries with any resolution fields specified are skipped.",
|
||||
"dd_mode_remapping_desc_5_sops_resolution_only": "\"Optimize game settings\" option must be enabled in the Moonlight client, otherwise the mapping is skipped.",
|
||||
"dd_mode_remapping_final_refresh_rate": "Final refresh rate",
|
||||
"dd_mode_remapping_final_resolution": "Final resolution",
|
||||
"dd_mode_remapping_requested_fps": "Requested FPS",
|
||||
"dd_mode_remapping_requested_resolution": "Requested resolution",
|
||||
"dd_options_header": "Advanced display device options",
|
||||
"dd_refresh_rate_option": "Refresh rate",
|
||||
"dd_refresh_rate_option_auto": "Use FPS value provided by the client (default)",
|
||||
|
|
|
|||
|
|
@ -274,3 +274,225 @@ TEST_P(ParseRefreshRateOption, IntegrationTest) {
|
|||
EXPECT_EQ(std::get<display_device::SingleDisplayConfiguration>(result).m_refresh_rate, expected_refresh_rate);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
using res_t = resolution_t;
|
||||
using fps_t = client_fps_t;
|
||||
using remap_entries_t = config::video_t::dd_t::mode_remapping_t;
|
||||
|
||||
struct no_value_t {
|
||||
};
|
||||
template <class T>
|
||||
struct auto_value_t {
|
||||
T value;
|
||||
};
|
||||
template <class T>
|
||||
struct manual_value_t {
|
||||
T value;
|
||||
};
|
||||
|
||||
using resolution_variant_t = std::variant<no_value_t, auto_value_t<res_t>, manual_value_t<res_t>>;
|
||||
using rational_variant_t = std::variant<no_value_t, auto_value_t<fps_t>, manual_value_t<fps_t>>;
|
||||
|
||||
struct failed_to_remap_t {};
|
||||
struct final_values_t {
|
||||
std::optional<resolution_t> resolution;
|
||||
std::optional<rational_t> refresh_rate;
|
||||
};
|
||||
|
||||
const std::string INVALID_RES { "INVALID" };
|
||||
const std::string INVALID_FPS { "1.23" };
|
||||
const std::string INVALID_REFRESH_RATE { "INVALID" };
|
||||
const remap_entries_t VALID_ENTRIES {
|
||||
.mixed = {
|
||||
{ "1920x1080", "11", "1024x720", "1.11" },
|
||||
{ "1920x1080", "", "1024x720", "2" },
|
||||
{ "", "33", "1024x720", "3" },
|
||||
{ "1920x720", "44", "1024x720", "" },
|
||||
{ "1920x720", "55", "", "5" },
|
||||
{ "1920x720", "", "1024x720", "" },
|
||||
{ "", "11", "", "7.77" } },
|
||||
.resolution_only = { { "1920x1080", "", "720x720", "" }, { "1024x720", "", "1920x1920", "" } },
|
||||
.refresh_rate_only = { { "", "11", "", "1.23" }, { "", "22", "", "2.34" } }
|
||||
};
|
||||
const remap_entries_t INVALID_REQ_RES {
|
||||
.mixed = { { INVALID_RES, "11", "1024x720", "1.11" } },
|
||||
.resolution_only = { { INVALID_RES, "", "720x720", "" } },
|
||||
.refresh_rate_only = { { INVALID_RES, "11", "", "1.23" } }
|
||||
};
|
||||
const remap_entries_t INVALID_REQ_FPS {
|
||||
.mixed = { { "1920x1080", INVALID_FPS, "1024x720", "1.11" } },
|
||||
.resolution_only = { { "1920x1080", INVALID_FPS, "720x720", "" } },
|
||||
.refresh_rate_only = { { "", INVALID_FPS, "", "1.23" } }
|
||||
};
|
||||
const remap_entries_t INVALID_FINAL_RES {
|
||||
.mixed = { { "1920x1080", "11", INVALID_RES, "1.11" } },
|
||||
.resolution_only = { { "1920x1080", "", INVALID_RES, "" } },
|
||||
.refresh_rate_only = { { "", "11", INVALID_RES, "1.23" } }
|
||||
};
|
||||
const remap_entries_t INVALID_FINAL_REFRESH_RATE {
|
||||
.mixed = { { "1920x1080", "11", "1024x720", INVALID_REFRESH_RATE } },
|
||||
.resolution_only = { { "1920x1080", "", "720x720", INVALID_REFRESH_RATE } },
|
||||
.refresh_rate_only = { { "", "11", "", INVALID_REFRESH_RATE } }
|
||||
};
|
||||
const remap_entries_t EMPTY_REQ_ENTRIES {
|
||||
.mixed = { { "", "", "1024x720", "1.11" } },
|
||||
.resolution_only = { { "", "", "720x720", "" } },
|
||||
.refresh_rate_only = { { "", "", "", "1.23" } }
|
||||
};
|
||||
const remap_entries_t EMPTY_FINAL_ENTRIES {
|
||||
.mixed = { { "1920x1080", "11", "", "" } },
|
||||
.resolution_only = { { "1920x1080", "", "", "" } },
|
||||
.refresh_rate_only = { { "", "11", "", "" } }
|
||||
};
|
||||
|
||||
using DisplayModeRemapping = DisplayDeviceConfigTest<std::pair<std::tuple<resolution_variant_t, rational_variant_t, sops_enabled_t, remap_entries_t>, std::variant<failed_to_remap_t, final_values_t>>>;
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DisplayDeviceConfigTest,
|
||||
DisplayModeRemapping,
|
||||
testing::Values(
|
||||
//---- Mixed (valid), SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1024, 720 } }, { { 111, 100 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 120 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1024, 720 } }, { { 2, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1, 1 }, auto_value_t<fps_t> { 33 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1024, 720 } }, { { 3, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 720 }, auto_value_t<fps_t> { 44 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1024, 720 } }, { { 44, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 720 }, auto_value_t<fps_t> { 55 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1920, 720 } }, { { 5, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 720 }, auto_value_t<fps_t> { 60 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1024, 720 } }, { { 60, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1, 1 }, auto_value_t<fps_t> { 123 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1, 1 } }, { { 123, 1 } } }),
|
||||
//---- Mixed (valid), SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 777, 100 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 120 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 120, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1, 1 }, auto_value_t<fps_t> { 33 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 33, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 720 }, auto_value_t<fps_t> { 44 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 44, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 720 }, auto_value_t<fps_t> { 55 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 55, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 720 }, auto_value_t<fps_t> { 60 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 60, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1, 1 }, auto_value_t<fps_t> { 123 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 123, 1 } } }),
|
||||
//---- Resolution only (valid), SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 720, 720 } }, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1024, 720 }, no_value_t {}, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1920, 1920 } }, std::nullopt }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 11, 11 }, manual_value_t<fps_t> { 33 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 11, 11 } }, { { 33, 1 } } }),
|
||||
//---- Resolution only (valid), SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1024, 720 }, no_value_t {}, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, std::nullopt }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 11, 11 }, manual_value_t<fps_t> { 33 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 33, 1 } } }),
|
||||
//---- Refresh rate only (valid), SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1920, 1080 } }, { { 123, 100 } } }),
|
||||
std::make_pair(std::make_tuple(no_value_t {}, auto_value_t<fps_t> { 22 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { std::nullopt, { { 234, 100 } } }),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 11, 11 }, auto_value_t<fps_t> { 33 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 11, 11 } }, { { 33, 1 } } }),
|
||||
//---- Refresh rate only (valid), SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 123, 100 } } }),
|
||||
std::make_pair(std::make_tuple(no_value_t {}, auto_value_t<fps_t> { 22 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 234, 100 } } }),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 11, 11 }, auto_value_t<fps_t> { 33 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 33, 1 } } }),
|
||||
//---- No mapping (valid), SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { { { 1920, 1080 } }, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(no_value_t {}, no_value_t {}, sops_enabled_t { true }, VALID_ENTRIES), final_values_t { std::nullopt, std::nullopt }),
|
||||
//---- No mapping (valid), SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(no_value_t {}, no_value_t {}, sops_enabled_t { false }, VALID_ENTRIES), final_values_t { std::nullopt, std::nullopt }),
|
||||
// ---- Invalid requested resolution, SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_REQ_RES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_REQ_RES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_REQ_RES), final_values_t { { { 1920, 1080 } }, { { 123, 100 } } }),
|
||||
// ---- Invalid requested resolution, SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_REQ_RES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_REQ_RES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_REQ_RES), final_values_t { std::nullopt, { { 123, 100 } } }),
|
||||
// ---- Invalid requested FPS, SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_REQ_FPS), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_REQ_FPS), final_values_t { { { 720, 720 } }, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_REQ_FPS), failed_to_remap_t {}),
|
||||
// ---- Invalid requested FPS, SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_REQ_FPS), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_REQ_FPS), final_values_t { std::nullopt, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_REQ_FPS), failed_to_remap_t {}),
|
||||
// ---- Invalid final resolution, SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_FINAL_RES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_FINAL_RES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_FINAL_RES), final_values_t { { { 1920, 1080 } }, { { 123, 100 } } }),
|
||||
// ---- Invalid final resolution, SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_FINAL_RES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_FINAL_RES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_FINAL_RES), final_values_t { std::nullopt, { { 123, 100 } } }),
|
||||
// ---- Invalid final refresh rate, SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_FINAL_REFRESH_RATE), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_FINAL_REFRESH_RATE), final_values_t { { { 720, 720 } }, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, INVALID_FINAL_REFRESH_RATE), failed_to_remap_t {}),
|
||||
// ---- Invalid final refresh rate, SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_FINAL_REFRESH_RATE), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_FINAL_REFRESH_RATE), final_values_t { std::nullopt, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, INVALID_FINAL_REFRESH_RATE), failed_to_remap_t {}),
|
||||
// ---- Empty req entries, SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, EMPTY_REQ_ENTRIES), final_values_t { { { 1024, 720 } }, { { 111, 100 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { true }, EMPTY_REQ_ENTRIES), final_values_t { { { 720, 720 } }, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, EMPTY_REQ_ENTRIES), final_values_t { { { 1920, 1080 } }, { { 123, 100 } } }),
|
||||
// ---- Empty req entries, SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, EMPTY_REQ_ENTRIES), final_values_t { std::nullopt, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { false }, EMPTY_REQ_ENTRIES), final_values_t { std::nullopt, { { 11, 1 } } }),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, EMPTY_REQ_ENTRIES), final_values_t { std::nullopt, { { 123, 100 } } }),
|
||||
// ---- Empty final entries, SOPS enabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, EMPTY_FINAL_ENTRIES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { true }, EMPTY_FINAL_ENTRIES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { true }, EMPTY_FINAL_ENTRIES), failed_to_remap_t {}),
|
||||
// ---- Empty final entries, SOPS disabled ----
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, EMPTY_FINAL_ENTRIES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(auto_value_t<res_t> { 1920, 1080 }, manual_value_t<fps_t> { 11 }, sops_enabled_t { false }, EMPTY_FINAL_ENTRIES), failed_to_remap_t {}),
|
||||
std::make_pair(std::make_tuple(manual_value_t<res_t> { 1920, 1080 }, auto_value_t<fps_t> { 11 }, sops_enabled_t { false }, EMPTY_FINAL_ENTRIES), failed_to_remap_t {})));
|
||||
TEST_P(DisplayModeRemapping, IntegrationTest) {
|
||||
const auto &[input_value, expected_value] = GetParam();
|
||||
const auto &[input_res, input_fps, input_enable_sops, input_entries] = input_value;
|
||||
|
||||
config::video_t video_config {};
|
||||
rtsp_stream::launch_session_t session {};
|
||||
|
||||
{ // resolution
|
||||
using enum resolution_option_e;
|
||||
|
||||
if (const auto *no_res { std::get_if<no_value_t>(&input_res) }; no_res) {
|
||||
video_config.dd.resolution_option = disabled;
|
||||
}
|
||||
else if (const auto *auto_res { std::get_if<auto_value_t<res_t>>(&input_res) }; auto_res) {
|
||||
video_config.dd.resolution_option = automatic;
|
||||
session.width = static_cast<int>(auto_res->value.m_width);
|
||||
session.height = static_cast<int>(auto_res->value.m_height);
|
||||
}
|
||||
else {
|
||||
const auto [manual_res] = std::get<manual_value_t<res_t>>(input_res);
|
||||
video_config.dd.resolution_option = manual;
|
||||
video_config.dd.manual_resolution = std::to_string(manual_res.m_width) + "x"s + std::to_string(manual_res.m_height);
|
||||
}
|
||||
}
|
||||
|
||||
{ // fps
|
||||
using enum refresh_rate_option_e;
|
||||
|
||||
if (const auto *no_fps { std::get_if<no_value_t>(&input_fps) }; no_fps) {
|
||||
video_config.dd.refresh_rate_option = disabled;
|
||||
}
|
||||
else if (const auto *auto_fps { std::get_if<auto_value_t<fps_t>>(&input_fps) }; auto_fps) {
|
||||
video_config.dd.refresh_rate_option = automatic;
|
||||
session.fps = auto_fps->value;
|
||||
}
|
||||
else {
|
||||
const auto [manual_fps] = std::get<manual_value_t<fps_t>>(input_fps);
|
||||
video_config.dd.refresh_rate_option = manual;
|
||||
video_config.dd.manual_refresh_rate = std::to_string(manual_fps);
|
||||
}
|
||||
}
|
||||
|
||||
video_config.dd.configuration_option = config_option_e::verify_only;
|
||||
video_config.dd.mode_remapping = input_entries;
|
||||
session.enable_sops = input_enable_sops;
|
||||
|
||||
const auto result { display_device::parse_configuration(video_config, session) };
|
||||
if (const auto *failed_option { std::get_if<failed_to_remap_t>(&expected_value) }; failed_option) {
|
||||
EXPECT_NO_THROW(std::get<display_device::failed_to_parse_tag_t>(result));
|
||||
}
|
||||
else {
|
||||
const auto &[expected_resolution, expected_refresh_rate] = std::get<final_values_t>(expected_value);
|
||||
const auto &parsed_config = std::get<display_device::SingleDisplayConfiguration>(result);
|
||||
|
||||
EXPECT_EQ(parsed_config.m_resolution, expected_resolution);
|
||||
EXPECT_EQ(parsed_config.m_refresh_rate, expected_refresh_rate ? std::make_optional(display_device::FloatingPoint { *expected_refresh_rate }) : std::nullopt);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue