From 2cab1be8eb99ad8b8d8b00a36558850191f34d3e Mon Sep 17 00:00:00 2001 From: mchughalex <25671999+mchughalex@users.noreply.github.com> Date: Fri, 17 Jul 2026 06:55:12 +0100 Subject: [PATCH] ui: device picker shows the auto-selected adapter; no wrap on stepper clicks --- include/rex/ui/graphics_device_list.h | 4 ++ src/ui/d3d12/d3d12_provider.cpp | 22 ++++++++ src/ui/overlay/simple_settings_overlay.cpp | 63 ++++++++++++++-------- src/ui/vulkan/vulkan_provider.cpp | 16 ++++++ 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/include/rex/ui/graphics_device_list.h b/include/rex/ui/graphics_device_list.h index 6a90d39..a3f9fa1 100644 --- a/include/rex/ui/graphics_device_list.h +++ b/include/rex/ui/graphics_device_list.h @@ -20,6 +20,10 @@ struct GraphicsDeviceList { // "vulkan_device"); empty when no backend has published a list. std::string cvar_name; std::vector 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); diff --git a/src/ui/d3d12/d3d12_provider.cpp b/src/ui/d3d12/d3d12_provider.cpp index b501e5c..caba550 100644 --- a/src/ui/d3d12/d3d12_provider.cpp +++ b/src/ui/d3d12/d3d12_provider.cpp @@ -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); diff --git a/src/ui/overlay/simple_settings_overlay.cpp b/src/ui/overlay/simple_settings_overlay.cpp index bcad22e..5fb45cd 100644 --- a/src/ui/overlay/simple_settings_overlay.cpp +++ b/src/ui/overlay/simple_settings_overlay.cpp @@ -220,16 +220,19 @@ std::vector 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(device_list.cvar_name); if (current >= 0 && current < static_cast(device_list.device_names.size())) { - return current + 1; + return current; } - return 0; + return std::clamp(device_list.active_index, 0, + static_cast(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(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(device_list_.device_names.size())); + device_index_ = std::clamp(device_index_, 0, + static_cast(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& 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 && - 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 && - 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); + // 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); + 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) { - step_row(row, -1); - } else if (right_hover) { - step_row(row, 1); + if (left_hit) { + if (can_left) { + step_row(row, -1); + } + } else if (right_hit) { + if (can_right) { + step_row(row, 1); + } } else if (hovered) { activate_row(row); // row-body click cycles with wrap-around } diff --git a/src/ui/vulkan/vulkan_provider.cpp b/src/ui/vulkan/vulkan_provider.cpp index 359e6f7..46a0831 100644 --- a/src/ui/vulkan/vulkan_provider.cpp +++ b/src/ui/vulkan/vulkan_provider.cpp @@ -165,6 +165,22 @@ std::unique_ptr 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_) {