ui: device picker shows the auto-selected adapter; no wrap on stepper clicks

This commit is contained in:
mchughalex 2026-07-17 06:55:12 +01:00
parent 855ed66c82
commit 2cab1be8eb
4 changed files with 82 additions and 23 deletions

View file

@ -20,6 +20,10 @@ struct GraphicsDeviceList {
// "vulkan_device"); empty when no backend has published a list.
std::string cvar_name;
std::vector<std::string> device_names;
// Index (in device_names / cvar order) of the device the backend actually
// selected this session; -1 while unknown. Lets UI show the device that
// automatic selection resolved to.
int32_t active_index = -1;
};
void SetGraphicsDeviceList(GraphicsDeviceList list);

View file

@ -345,6 +345,28 @@ bool D3D12Provider::Initialize() {
dxgi_factory->Release();
return false;
}
// Publish which cvar-order index the selection landed on (the automatic
// path walks a preference-ordered enumeration, so its index does not match
// the published list) so the settings device picker can show the adapter
// automatic selection resolved to. Matched by LUID against the plain
// enumeration the list was built from.
{
GraphicsDeviceList device_list = GetGraphicsDeviceList();
IDXGIAdapter1* match_adapter;
for (uint32_t i = 0; dxgi_factory->EnumAdapters1(i, &match_adapter) == S_OK; ++i) {
DXGI_ADAPTER_DESC1 match_desc;
const bool matched =
SUCCEEDED(match_adapter->GetDesc1(&match_desc)) &&
match_desc.AdapterLuid.LowPart == adapter_desc.AdapterLuid.LowPart &&
match_desc.AdapterLuid.HighPart == adapter_desc.AdapterLuid.HighPart;
match_adapter->Release();
if (matched) {
device_list.active_index = int32_t(i);
break;
}
}
SetGraphicsDeviceList(std::move(device_list));
}
adapter_vendor_id_ = GpuVendorID(adapter_desc.VendorId);
int adapter_name_mb_size =
WideCharToMultiByte(CP_UTF8, 0, adapter_desc.Description, -1, nullptr, 0, nullptr, nullptr);

View file

@ -220,16 +220,19 @@ std::vector<std::string_view> GetSimpleSettingsCvars() {
return cvars;
}
// 0 = automatic selection (cvar -1); i+1 = the list's device i.
// The picker lists devices directly (no separate automatic entry):
// automatic selection (cvar -1) reads as the device the backend reported
// actually picking.
int DeviceIndexFromCvar(const GraphicsDeviceList& device_list) {
if (device_list.device_names.empty() || !HasCvar(device_list.cvar_name)) {
return 0;
}
int32_t current = rex::cvar::Query<int32_t>(device_list.cvar_name);
if (current >= 0 && current < static_cast<int32_t>(device_list.device_names.size())) {
return current + 1;
return current;
}
return 0;
return std::clamp(device_list.active_index, 0,
static_cast<int32_t>(device_list.device_names.size()) - 1);
}
int ResolutionIndexFromScale(int32_t scale) {
@ -952,10 +955,14 @@ void SimpleSettingsDialog::SaveVideo() {
std::clamp(aspect_ratio_index_, 0, static_cast<int>(kAspectRatioLabels.size()) - 1);
field_of_view_ = std::clamp(field_of_view_, 40.0f, 120.0f);
if (!device_list_.device_names.empty() && HasCvar(device_list_.cvar_name)) {
device_index_ =
std::clamp(device_index_, 0, static_cast<int>(device_list_.device_names.size()));
device_index_ = std::clamp(device_index_, 0,
static_cast<int>(device_list_.device_names.size()) - 1);
// Selecting the device automatic selection already picked keeps the cvar
// automatic, so new hardware still re-picks the best adapter.
rex::cvar::SetFlagByName(device_list_.cvar_name,
device_index_ == 0 ? "-1" : std::to_string(device_index_ - 1));
device_index_ == device_list_.active_index
? "-1"
: std::to_string(device_index_));
}
const auto scale = std::to_string(kResolutionScales[resolution_scale_index_]);
rex::cvar::SetFlagByName("resolution_scale", scale);
@ -1058,19 +1065,20 @@ void SimpleSettingsDialog::BuildRows(std::vector<RowSpec>& rows, int category) {
row.kind = RowSpec::kEnum;
row.label = "Graphics Device";
row.desc =
"Select which GPU renders the game. Auto picks the highest-performance adapter.";
"Select which GPU renders the game. The highest-performance "
"adapter is selected automatically.";
// Index-prefixed labels: virtual display drivers (Parsec etc.) make
// the same physical GPU enumerate several times, and the index is
// what the device cvar and the startup log speak in.
row.options.push_back("Auto (recommended)");
// what the device cvar and the startup log speak in. No separate
// automatic entry; the row shows the adapter automatic selection
// resolved to, and re-selecting it keeps the choice automatic.
for (size_t i = 0; i < device_list_.device_names.size(); ++i) {
row.options.push_back(std::to_string(i) + ": " + device_list_.device_names[i]);
}
row.index = &device_index_;
if (device_index_ > 0 && device_index_ <= int(device_list_.device_names.size())) {
row.desc_extra = "Current: " + row.options[device_index_];
}
row.reset = [this] { device_index_ = 0; };
row.reset = [this] {
device_index_ = std::max(device_list_.active_index, 0);
};
rows.push_back(std::move(row));
}
{
@ -2333,12 +2341,17 @@ void SimpleSettingsDialog::OnDraw(ImGuiIO& io) {
// a circle greys out (and stops responding) at its end of the range.
const bool can_left = row.enabled && value > 0;
const bool can_right = row.enabled && value < count - 1;
bool left_hover = can_left &&
// Hit areas are tracked separately from enablement: a click on a
// greyed-out stepper must be swallowed, not fall through to the
// row-body wrap-around cycle below.
const bool left_hit =
mouse_in(left_center.x - chevron_r - 4.0f, cy - chevron_r - 4.0f,
left_center.x + chevron_r + 4.0f, cy + chevron_r + 4.0f);
bool right_hover = can_right &&
const bool right_hit =
mouse_in(right_center.x - chevron_r - 4.0f, cy - chevron_r - 4.0f,
right_center.x + chevron_r + 4.0f, cy + chevron_r + 4.0f);
bool left_hover = can_left && left_hit;
bool right_hover = can_right && right_hit;
const ImU32 chev_disabled = focused ? kColChevDisabledOnDark : kColChevDisabled;
DrawChevron(dl, left_center, chevron_r, true,
can_left ? (left_hover ? kColInteractHover : kColInteract)
@ -2369,10 +2382,14 @@ void SimpleSettingsDialog::OnDraw(ImGuiIO& io) {
text.c_str());
}
if (clicked && row.enabled) {
if (left_hover) {
if (left_hit) {
if (can_left) {
step_row(row, -1);
} else if (right_hover) {
}
} else if (right_hit) {
if (can_right) {
step_row(row, 1);
}
} else if (hovered) {
activate_row(row); // row-body click cycles with wrap-around
}

View file

@ -165,6 +165,22 @@ std::unique_ptr<VulkanProvider> VulkanProvider::Create(const bool with_gpu_emula
}
}
// Publish which enumeration index the selection landed on (the automatic
// path walks a preference-ordered copy of the list) so the settings device
// picker can show the device automatic selection resolved to.
{
const VkPhysicalDevice active_physical_device =
provider->vulkan_device_->physical_device();
for (size_t i = 0; i < physical_devices.size(); ++i) {
if (physical_devices[i] == active_physical_device) {
GraphicsDeviceList device_list = GetGraphicsDeviceList();
device_list.active_index = int32_t(i);
SetGraphicsDeviceList(std::move(device_list));
break;
}
}
}
if (with_presentation) {
provider->ui_samplers_ = UISamplers::Create(provider->vulkan_device_.get());
if (!provider->ui_samplers_) {