From 6e7e808b6614f98674dba7290bea2ea044c4d5d2 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Wed, 29 Jan 2025 02:02:46 -0600 Subject: [PATCH 01/13] DolphinQt/Mapping: Add setting to enable waiting for alternate mappings using the OR-operator. --- .../Config/Mapping/MappingCommon.cpp | 61 ++++++++++++------- .../Config/Mapping/MappingWindow.cpp | 19 ++++-- .../DolphinQt/Config/Mapping/MappingWindow.h | 6 +- .../ControllerInterface/CoreDevice.cpp | 6 +- .../ControllerInterface/CoreDevice.h | 7 ++- .../ControllerInterface/MappingCommon.cpp | 27 ++++++-- .../ControllerInterface/MappingCommon.h | 13 ++-- 7 files changed, 93 insertions(+), 46 deletions(-) diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp index edce169c28..37407f2298 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp @@ -19,8 +19,10 @@ namespace MappingCommon { constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3); -constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0); +constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(750); constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5); +// Ignore the mouse-click when queuing more buttons with "alternate mappings" enabled. +constexpr auto INPUT_DETECT_ENDING_IGNORE_TIME = std::chrono::milliseconds(50); class MappingProcessor : public QWidget { @@ -50,7 +52,7 @@ public: button->StartMapping(); std::vector device_strings{default_device.ToString()}; - if (m_parent->IsMappingAllDevices()) + if (m_parent->IsCreateOtherDeviceMappingsEnabled()) device_strings = g_controller_interface.GetAllDeviceStrings(); m_input_detector = std::make_unique(); @@ -63,35 +65,41 @@ public: if (!m_input_detector) return; - m_input_detector->Update(INPUT_DETECT_INITIAL_TIME, INPUT_DETECT_CONFIRMATION_TIME, + const auto confirmation_time = + INPUT_DETECT_CONFIRMATION_TIME * (m_parent->IsWaitForAlternateMappingsEnabled() ? 1 : 0); + + m_input_detector->Update(INPUT_DETECT_INITIAL_TIME, confirmation_time, INPUT_DETECT_MAXIMUM_TIME); if (m_input_detector->IsComplete()) { - auto detections = m_input_detector->TakeResults(); - ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections); - // No inputs detected. Cancel this and any other queued mappings. - if (detections.empty()) - { + if (!FinalizeMapping(m_input_detector->TakeResults())) CancelMapping(); - return; - } - - const auto& default_device = m_parent->GetController()->GetDefaultDevice(); - auto& button = m_clicked_mapping_buttons.front(); - auto* const control_reference = button->GetControlReference(); - - control_reference->SetExpression( - BuildExpression(detections, default_device, ciface::MappingCommon::Quote::On)); - m_parent->Save(); - - m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, - control_reference); - UnQueueInputDetection(button); } } + bool FinalizeMapping(ciface::Core::InputDetector::Results detections) + { + if (!ciface::MappingCommon::ContainsCompleteDetection(detections)) + return false; + + ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections); + + const auto& default_device = m_parent->GetController()->GetDefaultDevice(); + auto& button = m_clicked_mapping_buttons.front(); + auto* const control_reference = button->GetControlReference(); + + control_reference->SetExpression( + BuildExpression(detections, default_device, ciface::MappingCommon::Quote::On)); + m_parent->Save(); + + m_parent->GetController()->UpdateSingleControlReference(g_controller_interface, + control_reference); + UnQueueInputDetection(button); + return true; + } + void UpdateInputDetectionStartTimer() { m_input_detector.reset(); @@ -121,6 +129,15 @@ public: button->setText(QStringLiteral("[ ... ]")); m_clicked_mapping_buttons.push_back(button); + + if (m_input_detector) + { + // Ignore the mouse-click that queued this new detection and finalize the current mapping. + auto results = m_input_detector->TakeResults(); + ciface::MappingCommon::RemoveDetectionsAfterTimePoint( + &results, ciface::Core::DeviceContainer::Clock::now() - INPUT_DETECT_ENDING_IGNORE_TIME); + FinalizeMapping(std::move(results)); + } UpdateInputDetectionStartTimer(); } diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp index 46b4b1f302..665d5e9659 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp @@ -111,11 +111,15 @@ void MappingWindow::CreateDevicesLayout() const auto refresh_action = new QAction(tr("Refresh"), options); connect(refresh_action, &QAction::triggered, this, &MappingWindow::RefreshDevices); - m_all_devices_action = new QAction(tr("Create mappings for other devices"), options); - m_all_devices_action->setCheckable(true); + m_other_device_mappings = new QAction(tr("Create Mappings for Other Devices"), options); + m_other_device_mappings->setCheckable(true); + + m_wait_for_alternate_mappings = new QAction(tr("Wait for Alternate Input Mappings"), options); + m_wait_for_alternate_mappings->setCheckable(true); options->addAction(refresh_action); - options->addAction(m_all_devices_action); + options->addAction(m_other_device_mappings); + options->addAction(m_wait_for_alternate_mappings); options->setDefaultAction(refresh_action); m_devices_combo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); @@ -354,9 +358,14 @@ void MappingWindow::OnSelectDevice(int) m_controller->UpdateReferences(g_controller_interface); } -bool MappingWindow::IsMappingAllDevices() const +bool MappingWindow::IsCreateOtherDeviceMappingsEnabled() const { - return m_all_devices_action->isChecked(); + return m_other_device_mappings->isChecked(); +} + +bool MappingWindow::IsWaitForAlternateMappingsEnabled() const +{ + return m_wait_for_alternate_mappings->isChecked(); } void MappingWindow::RefreshDevices() diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.h b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.h index e51df977f3..10bf590f35 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.h +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.h @@ -51,7 +51,8 @@ public: int GetPort() const; ControllerEmu::EmulatedController* GetController() const; - bool IsMappingAllDevices() const; + bool IsCreateOtherDeviceMappingsEnabled() const; + bool IsWaitForAlternateMappingsEnabled() const; void ShowExtensionMotionTabs(bool show); void ActivateExtensionTab(); @@ -103,7 +104,8 @@ private: QGroupBox* m_devices_box; QHBoxLayout* m_devices_layout; QComboBox* m_devices_combo; - QAction* m_all_devices_action; + QAction* m_other_device_mappings; + QAction* m_wait_for_alternate_mappings; // Profiles QGroupBox* m_profiles_box; diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.cpp b/Source/Core/InputCommon/ControllerInterface/CoreDevice.cpp index b9141658e6..3a29d4c98f 100644 --- a/Source/Core/InputCommon/ControllerInterface/CoreDevice.cpp +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.cpp @@ -491,7 +491,7 @@ void InputDetector::Update(std::chrono::milliseconds initial_wait, Detection new_detection; new_detection.device = device_state.device; new_detection.input = input_state.input; - new_detection.press_time = Clock::now(); + new_detection.press_time = now; new_detection.smoothness = smoothness; // We found an input. Add it to our detections. @@ -516,12 +516,12 @@ bool InputDetector::IsComplete() const return !m_state; } -auto InputDetector::GetResults() const -> const std::vector& +auto InputDetector::GetResults() const -> const Results& { return m_detections; } -auto InputDetector::TakeResults() -> std::vector +auto InputDetector::TakeResults() -> Results { return std::move(m_detections); } diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h index 0370fce894..92f53df9fa 100644 --- a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h @@ -250,6 +250,7 @@ class InputDetector { public: using Detection = DeviceContainer::InputDetection; + using Results = std::vector; InputDetector(); ~InputDetector(); @@ -259,16 +260,16 @@ public: std::chrono::milliseconds maximum_wait); bool IsComplete() const; - const std::vector& GetResults() const; + const Results& GetResults() const; // move-return'd to prevent copying. - std::vector TakeResults(); + Results TakeResults(); private: struct Impl; Clock::time_point m_start_time; - std::vector m_detections; + Results m_detections; std::unique_ptr m_state; }; diff --git a/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp b/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp index cb06a7ed30..3765ef24a6 100644 --- a/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp +++ b/Source/Core/InputCommon/ControllerInterface/MappingCommon.cpp @@ -51,11 +51,10 @@ std::string GetExpressionForControl(const std::string& control_name, return expr; } -std::string -BuildExpression(const std::vector& detections, - const ciface::Core::DeviceQualifier& default_device, Quote quote) +std::string BuildExpression(const Core::InputDetector::Results& detections, + const ciface::Core::DeviceQualifier& default_device, Quote quote) { - std::vector pressed_inputs; + std::vector pressed_inputs; std::vector alternations; @@ -135,8 +134,7 @@ BuildExpression(const std::vector return fmt::to_string(fmt::join(alternations, "|")); } -void RemoveSpuriousTriggerCombinations( - std::vector* detections) +void RemoveSpuriousTriggerCombinations(Core::InputDetector::Results* detections) { const auto is_spurious = [&](const auto& detection) { return std::ranges::any_of(*detections, [&](const auto& d) { @@ -149,4 +147,21 @@ void RemoveSpuriousTriggerCombinations( std::erase_if(*detections, is_spurious); } +void RemoveDetectionsAfterTimePoint(Core::InputDetector::Results* results, + Core::DeviceContainer::Clock::time_point after) +{ + const auto is_after_time = [&](const Core::InputDetector::Detection& detection) { + return detection.release_time.value_or(after) >= after; + }; + + std::erase_if(*results, is_after_time); +} + +bool ContainsCompleteDetection(const Core::InputDetector::Results& results) +{ + return std::ranges::any_of(results, [](const Core::InputDetector::Detection& detection) { + return detection.release_time.has_value(); + }); +} + } // namespace ciface::MappingCommon diff --git a/Source/Core/InputCommon/ControllerInterface/MappingCommon.h b/Source/Core/InputCommon/ControllerInterface/MappingCommon.h index 1822125557..f58813a50c 100644 --- a/Source/Core/InputCommon/ControllerInterface/MappingCommon.h +++ b/Source/Core/InputCommon/ControllerInterface/MappingCommon.h @@ -17,13 +17,16 @@ enum class Quote }; std::string GetExpressionForControl(const std::string& control_name, - const ciface::Core::DeviceQualifier& control_device, - const ciface::Core::DeviceQualifier& default_device, + const Core::DeviceQualifier& control_device, + const Core::DeviceQualifier& default_device, Quote quote = Quote::On); -std::string BuildExpression(const std::vector&, - const ciface::Core::DeviceQualifier& default_device, Quote quote); +std::string BuildExpression(const Core::InputDetector::Results&, + const Core::DeviceQualifier& default_device, Quote quote); -void RemoveSpuriousTriggerCombinations(std::vector*); +void RemoveSpuriousTriggerCombinations(Core::InputDetector::Results*); +void RemoveDetectionsAfterTimePoint(Core::InputDetector::Results*, + Core::DeviceContainer::Clock::time_point after); +bool ContainsCompleteDetection(const Core::InputDetector::Results&); } // namespace ciface::MappingCommon From 698cc7aeb5342093678fb6f20f07d3f6b0329394 Mon Sep 17 00:00:00 2001 From: Xphalnos <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 1 Feb 2025 12:11:37 +0100 Subject: [PATCH 02/13] Externals: Vulkan 1.4 Support, xxHash 0.8.3 and VMA 3.2.0 --- Externals/Vulkan-Headers | 2 +- Externals/VulkanMemoryAllocator | 2 +- Externals/spirv_cross/SPIRV-Cross | 2 +- Externals/xxhash/xxHash | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Externals/Vulkan-Headers b/Externals/Vulkan-Headers index 05fe2cc910..39f924b810 160000 --- a/Externals/Vulkan-Headers +++ b/Externals/Vulkan-Headers @@ -1 +1 @@ -Subproject commit 05fe2cc910a68c9ba5dac07db46ef78573acee72 +Subproject commit 39f924b810e561fd86b2558b6711ca68d4363f68 diff --git a/Externals/VulkanMemoryAllocator b/Externals/VulkanMemoryAllocator index 009ecd192c..3bab692498 160000 --- a/Externals/VulkanMemoryAllocator +++ b/Externals/VulkanMemoryAllocator @@ -1 +1 @@ -Subproject commit 009ecd192c1289c7529bff248a16cfe896254816 +Subproject commit 3bab6924988e5f19bf36586a496156cf72f70d9f diff --git a/Externals/spirv_cross/SPIRV-Cross b/Externals/spirv_cross/SPIRV-Cross index 50b4d5389b..ebe2aa0cd8 160000 --- a/Externals/spirv_cross/SPIRV-Cross +++ b/Externals/spirv_cross/SPIRV-Cross @@ -1 +1 @@ -Subproject commit 50b4d5389b6a06f86fb63a2848e1a7da6d9755ca +Subproject commit ebe2aa0cd80f5eb5cd8a605da604cacf72205f3b diff --git a/Externals/xxhash/xxHash b/Externals/xxhash/xxHash index bbb27a5efb..e626a72bc2 160000 --- a/Externals/xxhash/xxHash +++ b/Externals/xxhash/xxHash @@ -1 +1 @@ -Subproject commit bbb27a5efb85b92a0486cf361a8635715a53f6ba +Subproject commit e626a72bc2321cd320e953a0ccf1584cad60f363 From c9bd6a13a9b08b1a61d04349e460681ef8087435 Mon Sep 17 00:00:00 2001 From: Xphalnos <164882787+Xphalnos@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:49:24 +0100 Subject: [PATCH 03/13] VideoBackends: Use DXGI 1.6 and D3D11_4 --- Source/Core/VideoBackends/D3D/D3DBase.h | 5 ++--- Source/Core/VideoBackends/D3D/D3DGfx.h | 3 ++- Source/Core/VideoBackends/D3D/D3DSwapChain.h | 3 +-- Source/Core/VideoBackends/D3D/D3DVertexManager.cpp | 2 +- Source/Core/VideoBackends/D3D/DXPipeline.h | 2 +- Source/Core/VideoBackends/D3D/DXTexture.h | 3 ++- Source/Core/VideoBackends/D3D12/D3D12Gfx.h | 1 + Source/Core/VideoBackends/D3D12/D3D12SwapChain.h | 1 - Source/Core/VideoBackends/D3D12/D3D12VertexManager.h | 1 + Source/Core/VideoBackends/D3D12/DX12Context.cpp | 2 +- Source/Core/VideoBackends/D3D12/DX12Shader.h | 1 + Source/Core/VideoBackends/D3D12/DX12Texture.h | 1 + Source/Core/VideoBackends/D3D12/DescriptorAllocator.h | 1 + Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h | 1 + Source/Core/VideoBackends/D3D12/VideoBackend.h | 1 + Source/Core/VideoBackends/D3DCommon/D3DCommon.cpp | 4 ++-- 16 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/D3DBase.h b/Source/Core/VideoBackends/D3D/D3DBase.h index 767237a5cd..a938340a7f 100644 --- a/Source/Core/VideoBackends/D3D/D3DBase.h +++ b/Source/Core/VideoBackends/D3D/D3DBase.h @@ -3,10 +3,9 @@ #pragma once -#include -#include +#include #include -#include +#include #include #include #include diff --git a/Source/Core/VideoBackends/D3D/D3DGfx.h b/Source/Core/VideoBackends/D3D/D3DGfx.h index 03104c25f4..96c5f316be 100644 --- a/Source/Core/VideoBackends/D3D/D3DGfx.h +++ b/Source/Core/VideoBackends/D3D/D3DGfx.h @@ -3,8 +3,9 @@ #pragma once -#include +#include #include + #include "VideoBackends/D3D/D3DState.h" #include "VideoCommon/AbstractGfx.h" diff --git a/Source/Core/VideoBackends/D3D/D3DSwapChain.h b/Source/Core/VideoBackends/D3D/D3DSwapChain.h index 4a6120b9b0..ea483ab671 100644 --- a/Source/Core/VideoBackends/D3D/D3DSwapChain.h +++ b/Source/Core/VideoBackends/D3D/D3DSwapChain.h @@ -3,8 +3,7 @@ #pragma once -#include -#include +#include #include #include diff --git a/Source/Core/VideoBackends/D3D/D3DVertexManager.cpp b/Source/Core/VideoBackends/D3D/D3DVertexManager.cpp index 5929788b03..124e30a182 100644 --- a/Source/Core/VideoBackends/D3D/D3DVertexManager.cpp +++ b/Source/Core/VideoBackends/D3D/D3DVertexManager.cpp @@ -3,7 +3,7 @@ #include "VideoBackends/D3D/D3DVertexManager.h" -#include +#include #include "Common/Align.h" #include "Common/Assert.h" diff --git a/Source/Core/VideoBackends/D3D/DXPipeline.h b/Source/Core/VideoBackends/D3D/DXPipeline.h index cc9fb74d56..a251b75fc1 100644 --- a/Source/Core/VideoBackends/D3D/DXPipeline.h +++ b/Source/Core/VideoBackends/D3D/DXPipeline.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include "VideoBackends/D3D/D3DBase.h" diff --git a/Source/Core/VideoBackends/D3D/DXTexture.h b/Source/Core/VideoBackends/D3D/DXTexture.h index 7997ca6f18..57a1165d4c 100644 --- a/Source/Core/VideoBackends/D3D/DXTexture.h +++ b/Source/Core/VideoBackends/D3D/DXTexture.h @@ -3,11 +3,12 @@ #pragma once -#include +#include #include #include #include #include + #include "Common/CommonTypes.h" #include "VideoBackends/D3D/D3DBase.h" diff --git a/Source/Core/VideoBackends/D3D12/D3D12Gfx.h b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h index 1f8e40314b..a852a46a0f 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12Gfx.h +++ b/Source/Core/VideoBackends/D3D12/D3D12Gfx.h @@ -4,6 +4,7 @@ #pragma once #include + #include "VideoBackends/D3D12/DescriptorAllocator.h" #include "VideoBackends/D3D12/DescriptorHeapManager.h" #include "VideoCommon/AbstractGfx.h" diff --git a/Source/Core/VideoBackends/D3D12/D3D12SwapChain.h b/Source/Core/VideoBackends/D3D12/D3D12SwapChain.h index 41c51029aa..e044412ade 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12SwapChain.h +++ b/Source/Core/VideoBackends/D3D12/D3D12SwapChain.h @@ -4,7 +4,6 @@ #pragma once #include -#include #include #include diff --git a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.h b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.h index 2dd2cdfee9..0d6f1811c6 100644 --- a/Source/Core/VideoBackends/D3D12/D3D12VertexManager.h +++ b/Source/Core/VideoBackends/D3D12/D3D12VertexManager.h @@ -4,6 +4,7 @@ #pragma once #include + #include "VideoBackends/D3D12/D3D12StreamBuffer.h" #include "VideoBackends/D3D12/DescriptorHeapManager.h" #include "VideoCommon/VertexManagerBase.h" diff --git a/Source/Core/VideoBackends/D3D12/DX12Context.cpp b/Source/Core/VideoBackends/D3D12/DX12Context.cpp index 574ce9c016..f3b481e8fc 100644 --- a/Source/Core/VideoBackends/D3D12/DX12Context.cpp +++ b/Source/Core/VideoBackends/D3D12/DX12Context.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include diff --git a/Source/Core/VideoBackends/D3D12/DX12Shader.h b/Source/Core/VideoBackends/D3D12/DX12Shader.h index ab6329d424..8c4c9292b4 100644 --- a/Source/Core/VideoBackends/D3D12/DX12Shader.h +++ b/Source/Core/VideoBackends/D3D12/DX12Shader.h @@ -6,6 +6,7 @@ #include #include #include + #include "VideoBackends/D3D12/Common.h" #include "VideoBackends/D3DCommon/Shader.h" diff --git a/Source/Core/VideoBackends/D3D12/DX12Texture.h b/Source/Core/VideoBackends/D3D12/DX12Texture.h index 2054b195b2..a64c1c9327 100644 --- a/Source/Core/VideoBackends/D3D12/DX12Texture.h +++ b/Source/Core/VideoBackends/D3D12/DX12Texture.h @@ -6,6 +6,7 @@ #include #include #include + #include "Common/CommonTypes.h" #include "VideoBackends/D3D12/Common.h" #include "VideoBackends/D3D12/DescriptorHeapManager.h" diff --git a/Source/Core/VideoBackends/D3D12/DescriptorAllocator.h b/Source/Core/VideoBackends/D3D12/DescriptorAllocator.h index bdc5f3996b..72b58b6cb5 100644 --- a/Source/Core/VideoBackends/D3D12/DescriptorAllocator.h +++ b/Source/Core/VideoBackends/D3D12/DescriptorAllocator.h @@ -4,6 +4,7 @@ #pragma once #include + #include "VideoBackends/D3D12/DescriptorHeapManager.h" #include "VideoCommon/Constants.h" diff --git a/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h b/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h index c7c00d518f..3abf849711 100644 --- a/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h +++ b/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h @@ -5,6 +5,7 @@ #include #include + #include "VideoBackends/D3D12/Common.h" #include "VideoCommon/RenderState.h" diff --git a/Source/Core/VideoBackends/D3D12/VideoBackend.h b/Source/Core/VideoBackends/D3D12/VideoBackend.h index db03b738d0..6167833c94 100644 --- a/Source/Core/VideoBackends/D3D12/VideoBackend.h +++ b/Source/Core/VideoBackends/D3D12/VideoBackend.h @@ -4,6 +4,7 @@ #pragma once #include + #include "VideoCommon/VideoBackendBase.h" namespace DX12 diff --git a/Source/Core/VideoBackends/D3DCommon/D3DCommon.cpp b/Source/Core/VideoBackends/D3DCommon/D3DCommon.cpp index 18406c2142..0658f1df8c 100644 --- a/Source/Core/VideoBackends/D3DCommon/D3DCommon.cpp +++ b/Source/Core/VideoBackends/D3DCommon/D3DCommon.cpp @@ -3,9 +3,9 @@ #include "VideoBackends/D3DCommon/D3DCommon.h" -#include +#include #include -#include +#include #include #include "Common/Assert.h" From 4a29e0e4f45bd1d6c8bfa2fbed22a0ae82cb29a1 Mon Sep 17 00:00:00 2001 From: Sintendo <3380580+Sintendo@users.noreply.github.com> Date: Sat, 1 Feb 2025 16:50:08 +0100 Subject: [PATCH 04/13] JitArm64_Integer: cmp - Subtract 12-bit constant You can encode a 12-bit immediate in a SUB instruction on ARM64. Constants in this range do not need to be sign extended, so we can exploit this to avoid materializing the immediate. This approach saves an instruction if it does not need to be materialized in a register afterwards. Otherwise, we just materialize it later and the total number of instructions stays the same. Before: 0x52800416 mov w22, #0x20 ; =32 0x93407f78 sxtw x24, w27 0xcb36c318 sub x24, x24, w22, sxtw After: 0x93407f78 sxtw x24, w27 0xd1008318 sub x24, x24, #0x20 --- Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp index 16cf25fdef..370ad619c5 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp @@ -652,9 +652,11 @@ void JitArm64::cmp(UGeckoInstruction inst) SXTW(CR, gpr.R(b)); MVN(CR, CR); } - else if (gpr.IsImm(b) && !gpr.GetImm(b)) + else if (gpr.IsImm(b) && (gpr.GetImm(b) & 0xFFF) == gpr.GetImm(b)) { SXTW(CR, gpr.R(a)); + if (const u32 imm = gpr.GetImm(b); imm != 0) + SUB(CR, CR, imm); } else { From 352cbc4772378d5048a2d8f80f5b5938f6946c5e Mon Sep 17 00:00:00 2001 From: Sintendo <3380580+Sintendo@users.noreply.github.com> Date: Sat, 1 Feb 2025 16:57:40 +0100 Subject: [PATCH 05/13] JitArm64_Integer: cmp - Subtract shifted 12-bit constant You can encode a shifted 12-bit immediate in a SUB instruction on ARM64. Constants in this range do not need to be sign extended, so we can exploit this to avoid materializing the immediate. This approach saves an instruction if it does not need to be materialized in a register afterwards. Otherwise, we just materialize it later and the total number of instructions stays the same. Before: 0x52a00099 mov w25, #0x40000 ; =262144 0x93407f7a sxtw x26, w27 0xcb39c35a sub x26, x26, w25, sxtw After: 0x93407f7a sxtw x26, w27 0xd141035a sub x26, x26, #0x40, lsl #12 ; =0x40000 --- Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp index 370ad619c5..3a56e58b84 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp @@ -658,6 +658,11 @@ void JitArm64::cmp(UGeckoInstruction inst) if (const u32 imm = gpr.GetImm(b); imm != 0) SUB(CR, CR, imm); } + else if (gpr.IsImm(b) && (gpr.GetImm(b) & 0xFFF000) == gpr.GetImm(b)) + { + SXTW(CR, gpr.R(a)); + SUB(CR, CR, gpr.GetImm(b) >> 12, true); + } else { ARM64Reg RA = gpr.R(a); From 01eed0a7585fbd3031e850ee8f14778e9e46a0e4 Mon Sep 17 00:00:00 2001 From: Sintendo <3380580+Sintendo@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:02:10 +0100 Subject: [PATCH 06/13] JitArm64_Integer: cmp - Add 12-bit constant You can encode a 12-bit immediate in an ADD instruction on ARM64. If the negated constant fits in this range, we can exploit this to avoid materializing the immediate. This approach saves an instruction if it does not need to be materialized in a register afterwards. Otherwise, we just materialize it later and the total number of instructions stays the same. Before: 0x12800019 mov w25, #-0x1 ; =-1 0x93407f5b sxtw x27, w26 0xcb39c37b sub x27, x27, w25, sxtw After: 0x93407f5b sxtw x27, w26 0x9100077b add x27, x27, #0x1 --- Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp index 3a56e58b84..2175f9a219 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp @@ -663,6 +663,11 @@ void JitArm64::cmp(UGeckoInstruction inst) SXTW(CR, gpr.R(a)); SUB(CR, CR, gpr.GetImm(b) >> 12, true); } + else if (gpr.IsImm(b) && (((~gpr.GetImm(b) + 1) & 0xFFF) == (~gpr.GetImm(b) + 1))) + { + SXTW(CR, gpr.R(a)); + ADD(CR, CR, ~gpr.GetImm(b) + 1); + } else { ARM64Reg RA = gpr.R(a); From 075c35602fd3d03b0f44363f01c44ff9cd81faa6 Mon Sep 17 00:00:00 2001 From: Sintendo <3380580+Sintendo@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:58:14 +0100 Subject: [PATCH 07/13] JitArm64_Integer: cmp - Add shifted 12-bit constant You can encode a shifted 12-bit immediate in an ADD instruction on ARM64. If the negated constant fits in this range, we can exploit this to avoid materializing the immediate. This approach saves an instruction if it does not need to be materialized in a register afterwards. Otherwise, we just materialize it later and the total number of instructions stays the same. Before: 0x52bff01a mov w26, #-0x800000 ; =-8388608 0x93407f1b sxtw x27, w24 0xcb3ac37b sub x27, x27, w26, sxtw After: 0x93407f1b sxtw x27, w24 0x9160037b add x27, x27, #0x800, lsl #12 ; =0x800000 --- Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp index 2175f9a219..242ed1eb42 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp @@ -668,6 +668,11 @@ void JitArm64::cmp(UGeckoInstruction inst) SXTW(CR, gpr.R(a)); ADD(CR, CR, ~gpr.GetImm(b) + 1); } + else if (gpr.IsImm(b) && (((~gpr.GetImm(b) + 1) & 0xFFF000) == (~gpr.GetImm(b) + 1))) + { + SXTW(CR, gpr.R(a)); + ADD(CR, CR, (~gpr.GetImm(b) + 1) >> 12, true); + } else { ARM64Reg RA = gpr.R(a); From c5870ed0c71d3821b792543631937110e2d27202 Mon Sep 17 00:00:00 2001 From: Sintendo <3380580+Sintendo@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:18:32 +0100 Subject: [PATCH 08/13] JitArm64_Integer: cmp - Skip sign extension if possible While we cannot always avoid materializing immediates, we can still inspect the most significant bit and potentially skip sign extension. This can sometimes save an instruction. Before: 0x5280003a mov w26, #0x1 ; =1 0x93407f5b sxtw x27, w26 0xcb38c37b sub x27, x27, w24, sxtw After: 0x5280003a mov w26, #0x1 ; =1 0xcb38c35b sub x27, x26, w24, sxtw Before: 0x52a20018 mov w24, #0x10000000 ; =268435456 0x93407f79 sxtw x25, w27 0xcb38c339 sub x25, x25, w24, sxtw After: 0x52a20018 mov w24, #0x10000000 ; =268435456 0x93407f79 sxtw x25, w27 0xcb180339 sub x25, x25, x24 --- .../PowerPC/JitArm64/JitArm64_Integer.cpp | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp index 242ed1eb42..518922343d 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp @@ -675,11 +675,35 @@ void JitArm64::cmp(UGeckoInstruction inst) } else { + // If we're dealing with immediates, check their most significant bit to + // see if we can skip sign extension. + const auto should_sign_extend = [&](u32 reg) -> bool { + return !gpr.IsImm(reg) || (gpr.GetImm(reg) & (1U << 31)); + }; + bool sign_extend_a = should_sign_extend(a); + bool sign_extend_b = should_sign_extend(b); + ARM64Reg RA = gpr.R(a); ARM64Reg RB = gpr.R(b); - SXTW(CR, RA); - SUB(CR, CR, RB, ArithOption(RB, ExtendSpecifier::SXTW)); + if (sign_extend_a) + { + SXTW(CR, RA); + RA = CR; + } + else + { + RA = EncodeRegTo64(RA); + } + + auto opt = ArithOption(RB, ExtendSpecifier::SXTW); + if (!sign_extend_b) + { + opt = ArithOption(CR, ShiftType::LSL, 0); + RB = EncodeRegTo64(RB); + } + + SUB(CR, RA, RB, opt); } } From b7c3f91643535b760a71de5b1faaae2b91ac7af2 Mon Sep 17 00:00:00 2001 From: Sintendo <3380580+Sintendo@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:11:23 +0100 Subject: [PATCH 09/13] JitArm64_Integer: cmpl - Subtract 12-bit constant You can encode a 12-bit immediate in a SUB instruction on ARM64. We can exploit this to avoid materializing the immediate. This approach saves an instruction if it does not need to be materialized in a register afterwards. Otherwise, we just materialize it later and the total number of instructions stays the same. Before: 0x5280003a mov w26, #0x1 ; =1 0xcb1a033b sub x27, x25, x26 After: 0xd100073b sub x27, x25, #0x1 --- Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp index 518922343d..ef44a72efd 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp @@ -728,9 +728,13 @@ void JitArm64::cmpl(UGeckoInstruction inst) { NEG(CR, EncodeRegTo64(gpr.R(b))); } - else if (gpr.IsImm(b) && !gpr.GetImm(b)) + else if (gpr.IsImm(b) && (gpr.GetImm(b) & 0xFFF) == gpr.GetImm(b)) { - MOV(EncodeRegTo32(CR), gpr.R(a)); + const u32 imm = gpr.GetImm(b); + if (imm == 0) + MOV(EncodeRegTo32(CR), gpr.R(a)); + else + SUB(CR, EncodeRegTo64(gpr.R(a)), imm); } else { From 7ce7da629e8059a7c819ea26436f4426648d22b5 Mon Sep 17 00:00:00 2001 From: Sintendo <3380580+Sintendo@users.noreply.github.com> Date: Sat, 1 Feb 2025 22:02:43 +0100 Subject: [PATCH 10/13] JitArm64_Integer: cmpl - Subtract shifted 12-bit constant You can encode a shifted 12-bit immediate in a SUB instruction on ARM64. We exploit this to avoid materializing the immediate. This approach saves an instruction if it does not need to be materialized in a register afterwards. Otherwise, we just materialize it later and the total number of instructions stays the same. Before: 0x52a00218 mov w24, #0x100000 ; =1048576 0xcb180379 sub x25, x27, x24 After: 0xd1440379 sub x25, x27, #0x100, lsl #12 ; =0x100000 --- Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp index ef44a72efd..58fab5cfea 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp @@ -736,6 +736,10 @@ void JitArm64::cmpl(UGeckoInstruction inst) else SUB(CR, EncodeRegTo64(gpr.R(a)), imm); } + else if (gpr.IsImm(b) && (gpr.GetImm(b) & 0xFFF000) == gpr.GetImm(b)) + { + SUB(CR, EncodeRegTo64(gpr.R(a)), gpr.GetImm(b) >> 12, true); + } else { SUB(CR, EncodeRegTo64(gpr.R(a)), EncodeRegTo64(gpr.R(b))); From 755c00326559904aee338d47a06409ec5b03b55e Mon Sep 17 00:00:00 2001 From: Sintendo <3380580+Sintendo@users.noreply.github.com> Date: Sat, 1 Feb 2025 22:05:24 +0100 Subject: [PATCH 11/13] JitArm64_RegCache: Const correctness Forgot this when I added it in #13120. --- Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h index b98e170531..5164745cd8 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h @@ -345,7 +345,7 @@ public: // Gets the immediate that a register is set to. Only valid for guest GPRs. u32 GetImm(size_t preg) const { return GetGuestGPROpArg(preg).GetImm(); } - bool IsImm(size_t preg, u32 imm) { return IsImm(preg) && GetImm(preg) == imm; } + bool IsImm(size_t preg, u32 imm) const { return IsImm(preg) && GetImm(preg) == imm; } // Binds a guest GPR to a host register, optionally loading its value. // From 5db6bd6054c6d535142102a3f54d8fa239f33b1c Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Tue, 11 Feb 2025 17:32:25 +0000 Subject: [PATCH 12/13] VideoCommon: simplify dither calculation This saves three instructions on AMD GPUs. Dunno about Nvidia. --- Source/Core/VideoCommon/PixelShaderGen.cpp | 4 ++-- Source/Core/VideoCommon/UberShaderPixel.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index a76681634f..0b027b2496 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -1308,9 +1308,9 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos if (uid_data->dither) { // Flipper uses a standard 2x2 Bayer Matrix for 6 bit dithering - // Here the matrix is encoded into the two factor constants out.Write("\tint2 dither = int2(rawpos.xy) & 1;\n"); - out.Write("\tprev.rgb = (prev.rgb - (prev.rgb >> 6)) + abs(dither.y * 3 - dither.x * 2);\n"); + out.Write( + "\tprev.rgb = (prev.rgb - (prev.rgb >> 6)) + (dither.x ^ dither.y) * 2 + dither.y;\n"); } WriteFog(out, uid_data); diff --git a/Source/Core/VideoCommon/UberShaderPixel.cpp b/Source/Core/VideoCommon/UberShaderPixel.cpp index ca8f42ec8f..f9674aa5a3 100644 --- a/Source/Core/VideoCommon/UberShaderPixel.cpp +++ b/Source/Core/VideoCommon/UberShaderPixel.cpp @@ -1434,8 +1434,8 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config, " // Flipper uses a standard 2x2 Bayer Matrix for 6 bit dithering\n" " // Here the matrix is encoded into the two factor constants\n" " int2 dither = int2(rawpos.xy) & 1;\n" - " TevResult.rgb = (TevResult.rgb - (TevResult.rgb >> 6)) + abs(dither.y * 3 - " - "dither.x * 2);\n" + " TevResult.rgb = (TevResult.rgb - (TevResult.rgb >> 6)) + (dither.x ^ dither.y) * " + "2 + dither.y;\n" " }}\n\n"); // ========= From adc5b81c318f2704fc0e68e51a16d5434111541a Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 7 Feb 2025 21:11:59 -0600 Subject: [PATCH 13/13] DolphinQt: Rename the pack/unpack SD Card buttons. --- .../Android/app/src/main/res/values/strings.xml | 8 ++++---- Source/Core/Common/FatFsUtil.h | 3 +++ Source/Core/Core/Core.cpp | 3 ++- Source/Core/DolphinQt/Settings/WiiPane.cpp | 16 ++++++++-------- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index aacc57a3be..0dbee76452 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -105,10 +105,10 @@ Synchronizes the SD Card with the SD Sync Folder when starting and ending emulation. SD Card Path SD Sync Folder - Convert Folder to File Now - You are about to convert the content of the SD sync folder into the SD card file. All current content of the file will be deleted. Are you sure you want to continue? - Convert File to Folder Now - You are about to convert the content of the SD card file into the SD sync folder. All current content of the folder will be deleted. Are you sure you want to continue? + Pack SD Card Now + You are about to pack the content of the SD sync folder into the SD card file. All current content of the file will be deleted. Are you sure you want to continue? + Unpack SD Card Now + You are about to unpack the content of the SD card file into the SD sync folder. All current content of the folder will be deleted. Are you sure you want to continue? Converting… Conversion done. Conversion failed. diff --git a/Source/Core/Common/FatFsUtil.h b/Source/Core/Common/FatFsUtil.h index df0ef58984..beb6c98208 100644 --- a/Source/Core/Common/FatFsUtil.h +++ b/Source/Core/Common/FatFsUtil.h @@ -9,6 +9,9 @@ namespace Common { +static constexpr auto SD_PACK_TEXT = "Pack SD Card Now"; +static constexpr auto SD_UNPACK_TEXT = "Unpack SD Card Now"; + bool SyncSDFolderToSDImage(const std::function& cancelled, bool deterministic); bool SyncSDImageToSDFolder(const std::function& cancelled); diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 84a398b7f7..e59c20a0d3 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -537,7 +537,8 @@ static void EmuThread(Core::System& system, std::unique_ptr boot PanicAlertFmtT( "Failed to sync SD card with folder. All changes made this session will be " "discarded on next boot if you do not manually re-issue a resync in Config > " - "Wii > SD Card Settings > Convert File to Folder Now!"); + "Wii > SD Card Settings > {0}!", + Common::GetStringT(Common::SD_UNPACK_TEXT)); } } }}; diff --git a/Source/Core/DolphinQt/Settings/WiiPane.cpp b/Source/Core/DolphinQt/Settings/WiiPane.cpp index 377d17c127..5d6bc23f7e 100644 --- a/Source/Core/DolphinQt/Settings/WiiPane.cpp +++ b/Source/Core/DolphinQt/Settings/WiiPane.cpp @@ -265,12 +265,12 @@ void WiiPane::CreateSDCard() sd_settings_group_layout->addWidget(m_sd_card_size_combo, row, 1); ++row; - m_sd_pack_button = new NonDefaultQPushButton(tr("Convert Folder to File Now")); - m_sd_unpack_button = new NonDefaultQPushButton(tr("Convert File to Folder Now")); + m_sd_pack_button = new NonDefaultQPushButton(tr(Common::SD_PACK_TEXT)); + m_sd_unpack_button = new NonDefaultQPushButton(tr(Common::SD_UNPACK_TEXT)); connect(m_sd_pack_button, &QPushButton::clicked, [this] { auto result = ModalMessageBox::warning( - this, tr("Convert Folder to File Now"), - tr("You are about to convert the content of the folder at %1 into the file at %2. All " + this, tr(Common::SD_PACK_TEXT), + tr("You are about to pack the content of the folder at %1 into the file at %2. All " "current content of the file will be deleted. Are you sure you want to continue?") .arg(QString::fromStdString(File::GetUserPath(D_WIISDCARDSYNCFOLDER_IDX))) .arg(QString::fromStdString(File::GetUserPath(F_WIISDCARDIMAGE_IDX))), @@ -289,13 +289,13 @@ void WiiPane::CreateSDCard() SetQWidgetWindowDecorations(progress_dialog.GetRaw()); progress_dialog.GetRaw()->exec(); if (!success.get()) - ModalMessageBox::warning(this, tr("Convert Folder to File Now"), tr("Conversion failed.")); + ModalMessageBox::warning(this, tr(Common::SD_PACK_TEXT), tr("Conversion failed.")); } }); connect(m_sd_unpack_button, &QPushButton::clicked, [this] { auto result = ModalMessageBox::warning( - this, tr("Convert File to Folder Now"), - tr("You are about to convert the content of the file at %2 into the folder at %1. All " + this, tr(Common::SD_UNPACK_TEXT), + tr("You are about to unpack the content of the file at %2 into the folder at %1. All " "current content of the folder will be deleted. Are you sure you want to continue?") .arg(QString::fromStdString(File::GetUserPath(D_WIISDCARDSYNCFOLDER_IDX))) .arg(QString::fromStdString(File::GetUserPath(F_WIISDCARDIMAGE_IDX))), @@ -314,7 +314,7 @@ void WiiPane::CreateSDCard() SetQWidgetWindowDecorations(progress_dialog.GetRaw()); progress_dialog.GetRaw()->exec(); if (!success.get()) - ModalMessageBox::warning(this, tr("Convert File to Folder Now"), tr("Conversion failed.")); + ModalMessageBox::warning(this, tr(Common::SD_UNPACK_TEXT), tr("Conversion failed.")); } }); sd_settings_group_layout->addWidget(m_sd_pack_button, row, 0, 1, 1);