diff --git a/Data/Sys/Shaders/default_pre_post_process.glsl b/Data/Sys/Shaders/default_pre_post_process.glsl index f6854d11fd..5df2de9986 100644 --- a/Data/Sys/Shaders/default_pre_post_process.glsl +++ b/Data/Sys/Shaders/default_pre_post_process.glsl @@ -173,7 +173,7 @@ float4 SharpBilinearSample(float3 uvw, float gamma) float2 texel = uvw.xy * source_size; float2 texel_floored = floor(texel); float2 s = fract(texel); - float scale = ceil(max(target_size.x * inverted_source_size.x, target_size.y * inverted_source_size.y)); + float scale = max(floor(max(target_size.x * inverted_source_size.x, target_size.y * inverted_source_size.y)), 1.f); float region_range = 0.5 - (0.5 / scale); // Figure out where in the texel to sample to get correct pre-scaled bilinear. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt index b637962177..63d72160c5 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/DocumentProvider.kt @@ -9,8 +9,11 @@ package org.dolphinemu.dolphinemu.features import android.annotation.TargetApi +import android.content.res.AssetFileDescriptor import android.database.Cursor import android.database.MatrixCursor +import android.graphics.Point +import android.net.Uri import android.os.Build import android.os.CancellationSignal import android.os.ParcelFileDescriptor @@ -27,7 +30,7 @@ class DocumentProvider : DocumentsProvider() { private var rootDirectory: File? = null companion object { - public const val ROOT_ID = "root" + const val ROOT_ID = "root" private val DEFAULT_ROOT_PROJECTION = arrayOf( DocumentsContract.Root.COLUMN_ROOT_ID, @@ -93,6 +96,8 @@ class DocumentProvider : DocumentsProvider() { appendDocument(file, result) } } + result.setNotificationUri(context!!.contentResolver, DocumentsContract.buildChildDocumentsUri( + "${context!!.packageName}.user", parentDocumentId)) return result } @@ -107,6 +112,16 @@ class DocumentProvider : DocumentsProvider() { return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode)) } + override fun openDocumentThumbnail( + documentId: String, + sizeHint: Point, + signal: CancellationSignal + ): AssetFileDescriptor { + val file = documentIdToPath(documentId) + val pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) + return AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH) + } + override fun createDocument( parentDocumentId: String, mimeType: String, @@ -121,6 +136,7 @@ class DocumentProvider : DocumentsProvider() { } else { file.createNewFile() } + refreshDocument(parentDocumentId) return pathToDocumentId(file) } @@ -128,7 +144,9 @@ class DocumentProvider : DocumentsProvider() { rootDirectory ?: return val file = documentIdToPath(documentId) + val fileParent = file.parentFile file.deleteRecursively() + refreshDocument(pathToDocumentId(fileParent!!)) } override fun renameDocument(documentId: String, displayName: String): String? { @@ -137,9 +155,19 @@ class DocumentProvider : DocumentsProvider() { val file = documentIdToPath(documentId) val dest = findFileNameForNewFile(File(file.parentFile, displayName)) file.renameTo(dest) + refreshDocument(pathToDocumentId(file.parentFile!!)) return pathToDocumentId(dest) } + private fun refreshDocument(parentDocumentId: String) { + val parentUri: Uri = + DocumentsContract.buildChildDocumentsUri( + "${context!!.packageName}.user", + parentDocumentId + ) + context!!.contentResolver.notifyChange(parentUri, null) + } + override fun isChildDocument(parentDocumentId: String, documentId: String): Boolean = documentId.startsWith(parentDocumentId) @@ -161,6 +189,10 @@ class DocumentProvider : DocumentsProvider() { } else { file.name } + val mimeType = getTypeForFile(file) + if (file.exists() && mimeType.startsWith("image/")) { + flags = flags or DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL + } cursor.newRow().apply { add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, pathToDocumentId(file)) add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(file)) diff --git a/Source/Core/Common/BitSet.h b/Source/Core/Common/BitSet.h index 5d2c88294d..87e0038554 100644 --- a/Source/Core/Common/BitSet.h +++ b/Source/Core/Common/BitSet.h @@ -73,7 +73,7 @@ public: else { int bit = std::countr_zero(m_val); - m_val &= ~(1 << bit); + m_val &= ~(IntTy{1} << bit); m_bit = bit; } return *this; @@ -98,15 +98,15 @@ public: constexpr BitSet(std::initializer_list init) { for (int bit : init) - m_val |= (IntTy)1 << bit; + m_val |= IntTy{1} << bit; } constexpr static BitSet AllTrue(size_t count) { - return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); + return BitSet(count == sizeof(IntTy) * 8 ? ~IntTy{0} : ((IntTy{1} << count) - 1)); } - Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); } + Ref operator[](size_t bit) { return Ref(this, IntTy{1} << bit); } constexpr const Ref operator[](size_t bit) const { return (*const_cast(this))[bit]; } constexpr bool operator==(BitSet other) const { return m_val == other.m_val; } constexpr bool operator!=(BitSet other) const { return m_val != other.m_val; } diff --git a/Source/Core/Common/CommonFuncs.cpp b/Source/Core/Common/CommonFuncs.cpp index 0a546428b7..aff87a9258 100644 --- a/Source/Core/Common/CommonFuncs.cpp +++ b/Source/Core/Common/CommonFuncs.cpp @@ -10,7 +10,10 @@ #ifdef _WIN32 #include + #define strerror_r(err, buf, len) strerror_s(buf, len, err) + +#include "Common/StringUtil.h" #endif namespace Common @@ -59,11 +62,11 @@ std::string GetLastErrorString() // Like GetLastErrorString() but if you have already queried the error code. std::string GetWin32ErrorString(DWORD error_code) { - char error_message[BUFFER_SIZE]; + wchar_t error_message[BUFFER_SIZE]; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error_code, + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error_message, BUFFER_SIZE, nullptr); - return std::string(error_message); + return WStringToUTF8(error_message); } // Obtains a full path to the specified module. diff --git a/Source/Core/Common/StringUtil.cpp b/Source/Core/Common/StringUtil.cpp index c9100ff11a..f960d6fcc6 100644 --- a/Source/Core/Common/StringUtil.cpp +++ b/Source/Core/Common/StringUtil.cpp @@ -426,7 +426,7 @@ size_t StringUTF8CodePointCount(std::string_view str) #ifdef _WIN32 -std::wstring CPToUTF16(u32 code_page, std::string_view input) +static std::wstring CPToUTF16(u32 code_page, std::string_view input) { auto const size = MultiByteToWideChar(code_page, 0, input.data(), static_cast(input.size()), nullptr, 0); @@ -444,7 +444,7 @@ std::wstring CPToUTF16(u32 code_page, std::string_view input) return output; } -std::string UTF16ToCP(u32 code_page, std::wstring_view input) +static std::string UTF16ToCP(u32 code_page, std::wstring_view input) { if (input.empty()) return {}; diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index ec5f4ef2c6..80b7ac72b9 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -950,6 +950,7 @@ struct ReverbPB // Base address of the circular buffer in MRAM. u16 circular_buffer_base_h; u16 circular_buffer_base_l; + DEFINE_32BIT_ACCESSOR(circular_buffer_base, CircularBufferBase) struct Destination { @@ -990,7 +991,7 @@ void ZeldaAudioRenderer::PrepareFrame() 0xB820); AddBuffersWithVolume(m_buf_front_left_reverb.data(), m_buf_back_right_reverb.data() + 0x28, 0x28, 0xB820); - AddBuffersWithVolume(m_buf_front_right_reverb.data(), m_buf_back_left_reverb.data() + 0x28, 0x28, + AddBuffersWithVolume(m_buf_front_right_reverb.data(), m_buf_back_right_reverb.data() + 0x28, 0x28, 0x7FFF); m_buf_back_left_reverb.fill(0); m_buf_back_right_reverb.fill(0); @@ -1059,8 +1060,7 @@ void ZeldaAudioRenderer::ApplyReverb(bool post_rendering) u16 mram_buffer_idx = m_reverb_pb_frames_count[rpb_idx]; - u32 mram_addr = ((rpb.circular_buffer_base_h << 16) | rpb.circular_buffer_base_l) + - mram_buffer_idx * 0x50 * sizeof(s16); + u32 mram_addr = rpb.GetCircularBufferBase() + mram_buffer_idx * 0x50 * sizeof(s16); s16* mram_ptr = (s16*)HLEMemory_Get_Pointer(mram_addr); if (!post_rendering) @@ -1217,11 +1217,9 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) // Compute reverb volume and ramp deltas. s16 reverb_volumes[4], reverb_volume_deltas[4]; - s16 reverb_volume_factor = - (vpb.dolby_volume_current * vpb.dolby_reverb_factor) >> (shift_factor - 1); for (size_t i = 0; i < 4; ++i) { - reverb_volumes[i] = (quadrant_volumes[i] * reverb_volume_factor) >> shift_factor; + reverb_volumes[i] = (quadrant_volumes[i] * vpb.dolby_reverb_factor) >> shift_factor; reverb_volume_deltas[i] = (volume_deltas[i] * vpb.dolby_reverb_factor) >> shift_factor; } diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index f59cb87e06..cbf9c2e991 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -577,8 +577,11 @@ void EmulationKernel::AddStaticDevices() } if (HasFeature(features, Feature::KD)) { - AddDevice(std::make_unique(*this, "/dev/net/kd/request")); - AddDevice(std::make_unique(*this, "/dev/net/kd/time")); + constexpr auto time_device_name = "/dev/net/kd/time"; + AddDevice(std::make_unique(*this, time_device_name)); + const auto time_device = + std::static_pointer_cast(GetDeviceByName(time_device_name)); + AddDevice(std::make_unique(*this, "/dev/net/kd/request", time_device)); } if (HasFeature(features, Feature::NCD)) { diff --git a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp index 3d4dc35613..56bda89f65 100644 --- a/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp +++ b/Source/Core/Core/IOS/Network/KD/NetKDRequest.cpp @@ -153,9 +153,10 @@ s32 NWC24MakeUserID(u64* nwc24_id, u32 hollywood_id, u16 id_ctr, HardwareModel h } } // Anonymous namespace -NetKDRequestDevice::NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name) +NetKDRequestDevice::NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name, + const std::shared_ptr& time_device) : EmulationDevice(ios, device_name), m_config{ios.GetFS()}, m_dl_list{ios.GetFS()}, - m_send_list{ios.GetFS()}, m_friend_list{ios.GetFS()} + m_send_list{ios.GetFS()}, m_friend_list{ios.GetFS()}, m_time_device{time_device} { // Enable all NWC24 permissions m_scheduler_buffer[1] = Common::swap32(-1); @@ -190,6 +191,8 @@ NetKDRequestDevice::~NetKDRequestDevice() } m_scheduler_timer_thread.join(); + m_scheduler_work_queue.Shutdown(); + m_work_queue.Shutdown(); } void NetKDRequestDevice::Update() @@ -441,9 +444,7 @@ NWC24::ErrorCode NetKDRequestDevice::DetermineDownloadTask(u16* entry_index, // As the scheduler does not tell us which entry to download, we must determine that. // A correct entry is one that hasn't been downloaded the longest compared to other entries. // We first need current UTC. - const auto time_device = - std::static_pointer_cast(GetIOS()->GetDeviceByName("/dev/net/kd/time")); - const u64 current_utc = time_device->GetAdjustedUTC(); + const u64 current_utc = m_time_device->GetAdjustedUTC(); u64 lowest_timestamp = std::numeric_limits::max(); for (u16 i = 0; i < static_cast(NWC24::NWC24Dl::MAX_ENTRIES); i++) @@ -493,9 +494,7 @@ NWC24::ErrorCode NetKDRequestDevice::DetermineSubtask(u16 entry_index, if (m_dl_list.IsSubtaskDownloadDisabled(entry_index)) return NWC24::WC24_ERR_DISABLED; - const auto time_device = - std::static_pointer_cast(GetIOS()->GetDeviceByName("/dev/net/kd/time")); - const u64 current_utc = time_device->GetAdjustedUTC(); + const u64 current_utc = m_time_device->GetAdjustedUTC(); for (u8 i = 0; i < 32; i++) { if (!m_dl_list.IsValidSubtask(entry_index, i)) @@ -645,9 +644,7 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index, { bool success = false; Common::ScopeGuard state_guard([&] { - const auto time_device = - std::static_pointer_cast(GetIOS()->GetDeviceByName("/dev/net/kd/time")); - const u64 current_utc = time_device->GetAdjustedUTC(); + const u64 current_utc = m_time_device->GetAdjustedUTC(); if (success) { // Set the next download time to the dl_margin diff --git a/Source/Core/Core/IOS/Network/KD/NetKDRequest.h b/Source/Core/Core/IOS/Network/KD/NetKDRequest.h index 0fcd43f52d..848454fa71 100644 --- a/Source/Core/Core/IOS/Network/KD/NetKDRequest.h +++ b/Source/Core/Core/IOS/Network/KD/NetKDRequest.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -17,6 +18,7 @@ #include "Core/IOS/Network/KD/Mail/WC24Send.h" #include "Core/IOS/Network/KD/NWC24Config.h" #include "Core/IOS/Network/KD/NWC24DL.h" +#include "Core/IOS/Network/KD/NetKDTime.h" namespace IOS::HLE { @@ -26,7 +28,8 @@ namespace IOS::HLE class NetKDRequestDevice : public EmulationDevice { public: - NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name); + NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name, + const std::shared_ptr& time_device); IPCReply HandleNWC24DownloadNowEx(const IOCtlRequest& request); NWC24::ErrorCode KDDownload(const u16 entry_index, const std::optional subtask_id); IPCReply HandleNWC24CheckMailNow(const IOCtlRequest& request); @@ -114,6 +117,7 @@ private: std::queue m_async_replies; u32 m_error_count = 0; std::array m_scheduler_buffer{}; + std::shared_ptr m_time_device; // TODO: Maybe move away from Common::HttpRequest? Common::HttpRequest m_http{std::chrono::minutes{1}}; u32 m_download_span = 2; diff --git a/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp b/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp index 525d65cf70..7b0e3e8242 100644 --- a/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp +++ b/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp @@ -221,6 +221,9 @@ void Jit64AsmRoutineManager::Generate() ABI_CallFunction(JitTrampoline); ABI_PopRegistersAndAdjustStack({}, 0); + // If jitting triggered an ISI exception, MSR.DR may have changed + MOV(64, R(RMEM), PPCSTATE(mem_ptr)); + JMP(dispatcher_no_check, Jump::Near); SetJumpTarget(bail); diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp index 1d8d3d1898..06d8d65b95 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp @@ -939,7 +939,6 @@ void JitArm64::dcbz(UGeckoInstruction inst) { INSTRUCTION_START JITDISABLE(bJITLoadStoreOff); - FALLBACK_IF(m_low_dcbz_hack); int a = inst.RA, b = inst.RB; diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp index 8538f2c040..cd270880f9 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp @@ -243,8 +243,9 @@ void Arm64GPRCache::FlushRegister(size_t index, bool maintain_state, ARM64Reg tm void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state, ARM64Reg tmp_reg) { - for (int i : regs) + for (auto iter = regs.begin(); iter != regs.end(); ++iter) { + const int i = *iter; ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded, "Attempted to flush discarded register"); @@ -269,7 +270,7 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state, ARM64Reg reg1.Flush(); reg2.Flush(); } - ++i; + ++iter; continue; } } diff --git a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp index 87f8f3ae2e..cd93fccebf 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp @@ -177,7 +177,12 @@ void JitArm64::GenerateAsm() // Call JIT ResetStack(); ABI_CallFunction(&JitTrampoline, this, DISPATCHER_PC); + LDR(IndexType::Unsigned, DISPATCHER_PC, PPC_REG, PPCSTATE_OFF(pc)); + + // If jitting triggered an ISI exception, MSR.DR may have changed + EmitUpdateMembase(); + B(dispatcher_no_check); SetJumpTarget(bail); diff --git a/Source/Core/Core/PowerPC/MMU.cpp b/Source/Core/Core/PowerPC/MMU.cpp index fdd370acc4..4574657bad 100644 --- a/Source/Core/Core/PowerPC/MMU.cpp +++ b/Source/Core/Core/PowerPC/MMU.cpp @@ -1343,7 +1343,8 @@ static TLBLookupResult LookupTLBPageAddress(PowerPC::PowerPCState& ppc_state, u32* paddr, bool* wi) { const u32 tag = vpa >> HW_PAGE_INDEX_SHIFT; - TLBEntry& tlbe = ppc_state.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK]; + const size_t tlb_index = IsOpcodeFlag(flag) ? PowerPC::INST_TLB_INDEX : PowerPC::DATA_TLB_INDEX; + TLBEntry& tlbe = ppc_state.tlb[tlb_index][tag & HW_PAGE_INDEX_MASK]; if (tlbe.tag[0] == tag && tlbe.vsid[0] == vsid) { @@ -1401,7 +1402,8 @@ static void UpdateTLBEntry(PowerPC::PowerPCState& ppc_state, const XCheckTLBFlag return; const u32 tag = address >> HW_PAGE_INDEX_SHIFT; - TLBEntry& tlbe = ppc_state.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK]; + const size_t tlb_index = IsOpcodeFlag(flag) ? PowerPC::INST_TLB_INDEX : PowerPC::DATA_TLB_INDEX; + TLBEntry& tlbe = ppc_state.tlb[tlb_index][tag & HW_PAGE_INDEX_MASK]; const u32 index = tlbe.recent == 0 && tlbe.tag[0] != TLBEntry::INVALID_TAG; tlbe.recent = index; tlbe.paddr[index] = pte2.RPN << HW_PAGE_INDEX_SHIFT; @@ -1414,8 +1416,8 @@ void MMU::InvalidateTLBEntry(u32 address) { const u32 entry_index = (address >> HW_PAGE_INDEX_SHIFT) & HW_PAGE_INDEX_MASK; - m_ppc_state.tlb[0][entry_index].Invalidate(); - m_ppc_state.tlb[1][entry_index].Invalidate(); + m_ppc_state.tlb[PowerPC::DATA_TLB_INDEX][entry_index].Invalidate(); + m_ppc_state.tlb[PowerPC::INST_TLB_INDEX][entry_index].Invalidate(); } // Page Address Translation diff --git a/Source/Core/Core/PowerPC/PowerPC.h b/Source/Core/Core/PowerPC/PowerPC.h index afcc0ac8bf..c473f20fd0 100644 --- a/Source/Core/Core/PowerPC/PowerPC.h +++ b/Source/Core/Core/PowerPC/PowerPC.h @@ -53,6 +53,8 @@ enum class CoreMode constexpr size_t TLB_SIZE = 128; constexpr size_t NUM_TLBS = 2; constexpr size_t TLB_WAYS = 2; +constexpr size_t DATA_TLB_INDEX = 0; +constexpr size_t INST_TLB_INDEX = 1; struct TLBEntry { diff --git a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp index 89665deb93..d2c619da28 100644 --- a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp @@ -184,7 +184,10 @@ void GeneralWidget::SaveSettings() { // Video Backend const auto current_backend = m_backend_combo->currentData().toString().toStdString(); - if (Config::Get(Config::MAIN_GFX_BACKEND) != current_backend) + if (Config::Get(Config::MAIN_GFX_BACKEND) == current_backend) + return; + + if (Config::GetActiveLayerForConfig(Config::MAIN_GFX_BACKEND) == Config::LayerType::Base) { auto warningMessage = VideoBackendBase::GetAvailableBackends()[m_backend_combo->currentIndex()] ->GetWarningMessage(); @@ -205,8 +208,10 @@ void GeneralWidget::SaveSettings() return; } } - emit BackendChanged(QString::fromStdString(current_backend)); } + + Config::SetBaseOrCurrent(Config::MAIN_GFX_BACKEND, current_backend); + emit BackendChanged(QString::fromStdString(current_backend)); } void GeneralWidget::OnEmulationStateChanged(bool running) @@ -217,6 +222,10 @@ void GeneralWidget::OnEmulationStateChanged(bool running) const bool supports_adapters = !g_Config.backend_info.Adapters.empty(); m_adapter_combo->setEnabled(!running && supports_adapters); + + std::string current_backend = m_backend_combo->currentData().toString().toStdString(); + if (Config::Get(Config::MAIN_GFX_BACKEND) != current_backend) + emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))); } void GeneralWidget::AddDescriptions() diff --git a/Source/Core/DolphinQt/Config/Graphics/GraphicsWindow.cpp b/Source/Core/DolphinQt/Config/Graphics/GraphicsWindow.cpp index 7b31cc7f6e..4edbea1594 100644 --- a/Source/Core/DolphinQt/Config/Graphics/GraphicsWindow.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/GraphicsWindow.cpp @@ -67,7 +67,6 @@ void GraphicsWindow::CreateMainLayout() void GraphicsWindow::OnBackendChanged(const QString& backend_name) { - Config::SetBase(Config::MAIN_GFX_BACKEND, backend_name.toStdString()); VideoBackendBase::PopulateBackendInfoFromUI(m_main_window->GetWindowSystemInfo()); setWindowTitle( diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp index cfb9483ba6..f9c4ffb29c 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp @@ -3,6 +3,8 @@ #include "DolphinQt/Config/Mapping/MappingWidget.h" +#include + #include #include #include @@ -25,6 +27,7 @@ #include "InputCommon/ControllerEmu/ControllerEmu.h" #include "InputCommon/ControllerEmu/Setting/NumericSetting.h" #include "InputCommon/ControllerEmu/StickGate.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" MappingWidget::MappingWidget(MappingWindow* parent) : m_parent(parent) { @@ -160,6 +163,26 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con [this, group] { ShowAdvancedControlGroupDialog(group); }); } + if (group->type == ControllerEmu::GroupType::Cursor) + { + QPushButton* mouse_button = new QPushButton(tr("Use Mouse Controlled Pointing")); + form_layout->insertRow(2, mouse_button); + connect(mouse_button, &QCheckBox::clicked, [this, group] { + std::string default_device = g_controller_interface.GetDefaultDeviceString() + ":"; + const std::string controller_device = GetController()->GetDefaultDevice().ToString() + ":"; + if (default_device == controller_device) + { + default_device.clear(); + } + group->SetControlExpression(0, fmt::format("`{}Cursor Y-`", default_device)); + group->SetControlExpression(1, fmt::format("`{}Cursor Y+`", default_device)); + group->SetControlExpression(2, fmt::format("`{}Cursor X-`", default_device)); + group->SetControlExpression(3, fmt::format("`{}Cursor X+`", default_device)); + emit ConfigChanged(); + GetController()->UpdateReferences(g_controller_interface); + }); + } + return group_box; } diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 7ba11fc5b7..b7a9c7f307 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -45,6 +45,8 @@ ControllerInterface g_controller_interface; // will never interfere with game threads. static thread_local ciface::InputChannel tls_input_channel = ciface::InputChannel::Host; +static thread_local bool tls_is_updating_devices = false; + void ControllerInterface::Initialize(const WindowSystemInfo& wsi) { if (m_is_init) @@ -122,8 +124,8 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) // We lock m_devices_population_mutex here to make everything simpler. // Multiple devices classes have their own "hotplug" thread, and can add/remove devices at any // time, while actual writes to "m_devices" are safe, the order in which they happen is not. That - // means a thread could be adding devices while we are removing them, or removing them as we are - // populating them (causing missing or duplicate devices). + // means a thread could be adding devices while we are removing them from a different thread, + // or removing them as we are populating them (causing missing or duplicate devices). std::lock_guard lk_population(m_devices_population_mutex); #if defined(CIFACE_USE_WIN32) && !defined(CIFACE_USE_XLIB) && !defined(CIFACE_USE_OSX) @@ -271,6 +273,10 @@ bool ControllerInterface::AddDevice(std::shared_ptr device if (!m_is_init) return false; + ASSERT_MSG(CONTROLLERINTERFACE, !tls_is_updating_devices, + "Devices shouldn't be added within input update calls, there is a risk of deadlock " + "if another thread was already here"); + std::lock_guard lk_population(m_devices_population_mutex); { @@ -328,6 +334,10 @@ void ControllerInterface::RemoveDevice(std::function> devices_to_remove; - // Lock this first to avoid deadlock with m_devices_mutex in certain cases (such as a Wii Remote - // getting disconnected) - if (!m_devices_population_mutex.try_lock()) - return; - - std::lock_guard population_lock(m_devices_population_mutex, std::adopt_lock); - - if (!m_devices_mutex.try_lock()) - return; - - std::lock_guard lk(m_devices_mutex, std::adopt_lock); - - for (auto& backend : m_input_backends) - backend->UpdateInput(); - - for (const auto& d : m_devices) { - // Theoretically we could avoid updating input on devices that don't have any references to - // them, but in practice a few devices types could break in different ways, so we don't - d->UpdateInput(); + // TODO: if we are an emulation input channel, we should probably always lock. + // Prefer outdated values over blocking UI or CPU thread (this avoids short but noticeable frame + // drops) + if (!m_devices_mutex.try_lock()) + return; + + std::lock_guard lk_devices(m_devices_mutex, std::adopt_lock); + + tls_is_updating_devices = true; + + for (auto& backend : m_input_backends) + backend->UpdateInput(devices_to_remove); + + for (const auto& d : m_devices) + { + // Theoretically we could avoid updating input on devices that don't have any references to + // them, but in practice a few devices types could break in different ways, so we don't + if (d->UpdateInput() == ciface::Core::DeviceRemoval::Remove) + devices_to_remove.push_back(d); + } + + tls_is_updating_devices = false; + } + + if (devices_to_remove.size() > 0) + { + RemoveDevice([&](const ciface::Core::Device* device) { + return std::any_of(devices_to_remove.begin(), devices_to_remove.end(), + [device](const std::weak_ptr& d) { + return d.lock().get() == device; + }); + }); } } diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 6dc0afafe8..837e5135d5 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -127,7 +127,6 @@ private: std::list> m_devices_changed_callbacks; mutable std::recursive_mutex m_devices_population_mutex; - mutable std::mutex m_pre_population_mutex; mutable std::mutex m_callbacks_mutex; std::atomic m_is_init; // This is now always protected by m_devices_population_mutex, so diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h index a2176637ab..1667b42a00 100644 --- a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h @@ -36,6 +36,12 @@ constexpr ControlState BATTERY_INPUT_MAX_VALUE = 100.0; namespace Core { +enum class DeviceRemoval +{ + Remove, + Keep, +}; + class Device { public: @@ -118,7 +124,7 @@ public: virtual std::string GetName() const = 0; virtual std::string GetSource() const = 0; std::string GetQualifiedName() const; - virtual void UpdateInput() {} + virtual DeviceRemoval UpdateInput() { return DeviceRemoval::Keep; } // May be overridden to implement hotplug removal. // Currently handled on a per-backend basis but this could change. @@ -242,7 +248,8 @@ public: std::recursive_mutex& GetDevicesMutex() const { return m_devices_mutex; } protected: - // Exclusively needed when reading/writing "m_devices" + // Exclusively needed when reading/writing the "m_devices" array. + // Not needed when individually readring/writing a single device ptr. mutable std::recursive_mutex m_devices_mutex; std::vector> m_devices; }; diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp index eb60721b2f..f095818287 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp @@ -222,7 +222,7 @@ bool Joystick::IsValid() const return SUCCEEDED(m_device->Acquire()); } -void Joystick::UpdateInput() +Core::DeviceRemoval Joystick::UpdateInput() { HRESULT hr = 0; @@ -261,6 +261,8 @@ void Joystick::UpdateInput() // try reacquire if input lost if (DIERR_INPUTLOST == hr || DIERR_NOTACQUIRED == hr) m_device->Acquire(); + + return Core::DeviceRemoval::Keep; } // get name diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h index f69b95293c..cb73f373f8 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h @@ -57,7 +57,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; Joystick(const LPDIRECTINPUTDEVICE8 device); ~Joystick(); diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp index 551afde423..3c6a82dcd8 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.cpp @@ -205,7 +205,7 @@ void KeyboardMouse::UpdateCursorInput() m_state_in.cursor.y = (ControlState(point.y) / win_height * 2 - 1) * window_scale.y; } -void KeyboardMouse::UpdateInput() +Core::DeviceRemoval KeyboardMouse::UpdateInput() { UpdateCursorInput(); @@ -254,6 +254,8 @@ void KeyboardMouse::UpdateInput() else INFO_LOG_FMT(CONTROLLERINTERFACE, "Keyboard device failed to re-acquire, we'll retry later"); } + + return Core::DeviceRemoval::Keep; } std::string KeyboardMouse::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h index eba4c8ab93..e7187849f6 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputKeyboardMouse.h @@ -94,7 +94,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device); ~KeyboardMouse(); diff --git a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp index d3030a60fa..8a5abb16d9 100644 --- a/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp @@ -128,7 +128,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; Device(std::string name, int index, std::string server_address, u16 server_port, u32 client_uid); @@ -614,7 +614,7 @@ std::string Device::GetSource() const return std::string(DUALSHOCKUDP_SOURCE_NAME); } -void Device::UpdateInput() +Core::DeviceRemoval Device::UpdateInput() { // Regularly tell the UDP server to feed us controller data const auto now = SteadyClock::now(); @@ -660,6 +660,8 @@ void Device::UpdateInput() m_prev_touch_valid = true; } } + + return Core::DeviceRemoval::Keep; } std::optional Device::GetPreferredId() const diff --git a/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp b/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp index 91685eea00..422d7e911c 100644 --- a/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp +++ b/Source/Core/InputCommon/ControllerInterface/InputBackend.cpp @@ -12,7 +12,7 @@ InputBackend::InputBackend(ControllerInterface* controller_interface) InputBackend::~InputBackend() = default; -void InputBackend::UpdateInput() +void InputBackend::UpdateInput(std::vector>& devices_to_remove) { } diff --git a/Source/Core/InputCommon/ControllerInterface/InputBackend.h b/Source/Core/InputCommon/ControllerInterface/InputBackend.h index 653bc16df1..80ced7e194 100644 --- a/Source/Core/InputCommon/ControllerInterface/InputBackend.h +++ b/Source/Core/InputCommon/ControllerInterface/InputBackend.h @@ -3,10 +3,19 @@ #pragma once +#include +#include + class ControllerInterface; namespace ciface { + +namespace Core +{ +class Device; +} + class InputBackend { public: @@ -15,7 +24,9 @@ public: virtual ~InputBackend(); virtual void PopulateDevices() = 0; - virtual void UpdateInput(); + // Do NOT directly add/remove devices within here, + // just add them to the removal list if necessary. + virtual void UpdateInput(std::vector>& devices_to_remove); ControllerInterface& GetControllerInterface(); diff --git a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp index 5c71adec23..af7ad7dc6f 100644 --- a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.cpp @@ -86,7 +86,7 @@ PipeDevice::~PipeDevice() close(m_fd); } -void PipeDevice::UpdateInput() +Core::DeviceRemoval PipeDevice::UpdateInput() { // Read any pending characters off the pipe. If we hit a newline, // then dequeue a command off the front of m_buf and parse it. @@ -105,6 +105,7 @@ void PipeDevice::UpdateInput() m_buf.erase(0, newline + 1); newline = m_buf.find("\n"); } + return Core::DeviceRemoval::Keep; } void PipeDevice::AddAxis(const std::string& name, double value) diff --git a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h index ab9e987c32..53fefd0cb5 100644 --- a/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h +++ b/Source/Core/InputCommon/ControllerInterface/Pipes/Pipes.h @@ -29,7 +29,7 @@ public: PipeDevice(int fd, const std::string& name); ~PipeDevice(); - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; std::string GetName() const override { return m_name; } std::string GetSource() const override { return "Pipe"; } diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h index e4bdb6e5fb..07292f9039 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h @@ -62,7 +62,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; explicit KeyboardAndMouse(void* view); ~KeyboardAndMouse() override; diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm index 5ea4ff2624..e41c370edf 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -236,7 +236,7 @@ void KeyboardAndMouse::MainThreadInitialization(void* view) m_window_pos_observer = [[DolWindowPositionObserver alloc] initWithView:cocoa_view]; } -void KeyboardAndMouse::UpdateInput() +Core::DeviceRemoval KeyboardAndMouse::UpdateInput() { NSRect bounds = [m_window_pos_observer frame]; @@ -268,6 +268,8 @@ void KeyboardAndMouse::UpdateInput() m_cursor.x = (loc.x / window_width * 2 - 1.0) * window_scale.x; m_cursor.y = (loc.y / window_height * 2 - 1.0) * -window_scale.y; } + + return Core::DeviceRemoval::Keep; } std::string KeyboardAndMouse::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp index ac0123aedd..3bf4020ad5 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp @@ -18,6 +18,11 @@ #include #endif +namespace ciface::Core +{ +class Device; +} + namespace ciface::SDL { static std::string GetJoystickName(int index) @@ -35,7 +40,7 @@ public: InputBackend(ControllerInterface* controller_interface); ~InputBackend(); void PopulateDevices() override; - void UpdateInput() override; + void UpdateInput(std::vector>& devices_to_remove) override; private: void OpenAndAddDevice(int index); @@ -637,7 +642,7 @@ void Joystick::Motor::SetState(ControlState state) } #endif -void InputBackend::UpdateInput() +void InputBackend::UpdateInput(std::vector>& devices_to_remove) { SDL_JoystickUpdate(); } diff --git a/Source/Core/InputCommon/ControllerInterface/SteamDeck/SteamDeck.cpp b/Source/Core/InputCommon/ControllerInterface/SteamDeck/SteamDeck.cpp index 0a0dc88387..7cd76d0710 100644 --- a/Source/Core/InputCommon/ControllerInterface/SteamDeck/SteamDeck.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SteamDeck/SteamDeck.cpp @@ -113,7 +113,7 @@ public: Device(hid_device* device); std::string GetName() const final override; std::string GetSource() const final override; - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; private: hid_device* m_device; @@ -279,7 +279,7 @@ std::string Device::GetSource() const return std::string(STEAMDECK_SOURCE_NAME); } -void Device::UpdateInput() +Core::DeviceRemoval Device::UpdateInput() { // As of a certain mid-2023 update to the Steam client, // Steam will disable gyro data if gyro is not mapped in Steam Input. @@ -308,16 +308,18 @@ void Device::UpdateInput() } // In case there were no reports available to be read, bail early. if (!got_anything) - return; + return Core::DeviceRemoval::Keep; if (rpt.major_ver != 0x01 || rpt.minor_ver != 0x00 || rpt.report_type != 0x09 || rpt.report_sz != sizeof(rpt)) { ERROR_LOG_FMT(CONTROLLERINTERFACE, "Steam Deck bad report"); - return; + return Core::DeviceRemoval::Keep; } m_latest_input = rpt; + + return Core::DeviceRemoval::Keep; } } // namespace ciface::SteamDeck diff --git a/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp b/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp index b31c5f1f8e..14c1072992 100644 --- a/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp +++ b/Source/Core/InputCommon/ControllerInterface/WGInput/WGInput.cpp @@ -490,7 +490,7 @@ private: std::string GetSource() const override { return std::string(SOURCE_NAME); } - void UpdateInput() override + Core::DeviceRemoval UpdateInput() override { // IRawGameController: static_assert(sizeof(bool) == sizeof(ButtonValueType)); @@ -527,6 +527,8 @@ private: // IGameControllerBatteryInfo: if (!UpdateBatteryLevel()) DEBUG_LOG_FMT(CONTROLLERINTERFACE, "WGInput: UpdateBatteryLevel failed."); + + return Core::DeviceRemoval::Keep; } void UpdateMotors() diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp index 6b92bb9b16..bc2db53328 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.cpp @@ -1395,14 +1395,10 @@ void Device::UpdateRumble() QueueReport(OutputReportRumble{}); } -void Device::UpdateInput() +Core::DeviceRemoval Device::UpdateInput() { if (!m_wiimote->IsConnected()) - { - g_controller_interface.RemoveDevice( - [this](const Core::Device* device) { return device == this; }); - return; - } + return Core::DeviceRemoval::Remove; UpdateRumble(); RunTasks(); @@ -1413,6 +1409,8 @@ void Device::UpdateInput() ProcessInputReport(report); RunTasks(); } + + return Core::DeviceRemoval::Keep; } void Device::MotionPlusState::ProcessData(const WiimoteEmu::MotionPlus::DataFormat& data) diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h index f650f68b46..bbc07e58be 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h +++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/WiimoteController.h @@ -35,7 +35,7 @@ public: std::string GetSource() const override; int GetSortPriority() const override; - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; private: using Clock = std::chrono::steady_clock; diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp index 82769cb4f4..ebe79f8984 100644 --- a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp @@ -264,7 +264,7 @@ std::string Device::GetSource() const return "XInput"; } -void Device::UpdateInput() +Core::DeviceRemoval Device::UpdateInput() { PXInputGetState(m_index, &m_state_in); @@ -286,6 +286,8 @@ void Device::UpdateInput() break; } } + + return Core::DeviceRemoval::Keep; } void Device::UpdateMotors() diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h index 1673f795ed..95fe6a3c6f 100644 --- a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h @@ -33,7 +33,7 @@ public: std::optional GetPreferredId() const override; int GetSortPriority() const override { return -2; } - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; void UpdateMotors(); diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index d90139fe03..f041dd16a1 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -275,7 +275,7 @@ void KeyboardMouse::UpdateCursor(bool should_center_mouse) m_state.cursor.y = (win_y / win_height * 2 - 1) * window_scale.y; } -void KeyboardMouse::UpdateInput() +Core::DeviceRemoval KeyboardMouse::UpdateInput() { XFlush(m_display); @@ -369,6 +369,8 @@ void KeyboardMouse::UpdateInput() if (update_keyboard) XQueryKeymap(m_display, m_state.keyboard.data()); + + return Core::DeviceRemoval::Keep; } std::string KeyboardMouse::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index 6a4f8436f1..a8960c1d23 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -111,7 +111,7 @@ private: void UpdateCursor(bool should_center_mouse); public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid, double scroll_increment); diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp index cf811e5b41..6e9764fda9 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp @@ -674,7 +674,7 @@ void InputBackend::RemoveDevnodeObject(const std::string& node) m_devnode_objects.erase(node); } -void evdevDevice::UpdateInput() +Core::DeviceRemoval evdevDevice::UpdateInput() { // Run through all evdev events // libevdev will keep track of the actual controller state internally which can be queried @@ -691,6 +691,7 @@ void evdevDevice::UpdateInput() rc = libevdev_next_event(node.device, LIBEVDEV_READ_FLAG_NORMAL, &ev); } } + return Core::DeviceRemoval::Keep; } bool evdevDevice::IsValid() const diff --git a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h index ff5ab8a72a..f32567af8c 100644 --- a/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h +++ b/Source/Core/InputCommon/ControllerInterface/evdev/evdev.h @@ -72,7 +72,7 @@ private: }; public: - void UpdateInput() override; + Core::DeviceRemoval UpdateInput() override; bool IsValid() const override; evdevDevice(InputBackend* input_backend); diff --git a/Source/Core/VideoCommon/TextureConverterShaderGen.cpp b/Source/Core/VideoCommon/TextureConverterShaderGen.cpp index 92d30895fb..da2e247a7f 100644 --- a/Source/Core/VideoCommon/TextureConverterShaderGen.cpp +++ b/Source/Core/VideoCommon/TextureConverterShaderGen.cpp @@ -19,6 +19,26 @@ TCShaderUid GetShaderUid(EFBCopyFormat dst_format, bool is_depth_copy, bool is_i TCShaderUid out; UidData* const uid_data = out.GetUidData(); + if (g_ActiveConfig.bForceTrueColor) + { + // Increase the precision of EFB copies where it's likely to be safe. + switch (dst_format) + { + case EFBCopyFormat::RGB565: + // HACK: XFB is RGB8. + // Don't blindly do this in other places though, + // the enum value is used to identify XFB copies. + // The important thing here is that we need alpha = 1. + dst_format = EFBCopyFormat::XFB; + break; + case EFBCopyFormat::RGB5A3: + dst_format = EFBCopyFormat::RGBA8; + break; + default: + // Let's not touch the other formats for now, seems risky. + break; + } + } uid_data->dst_format = dst_format; uid_data->efb_has_alpha = bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24; uid_data->is_depth_copy = is_depth_copy; diff --git a/Source/UnitTests/Common/BitSetTest.cpp b/Source/UnitTests/Common/BitSetTest.cpp index 21c211516c..397f9fecbe 100644 --- a/Source/UnitTests/Common/BitSetTest.cpp +++ b/Source/UnitTests/Common/BitSetTest.cpp @@ -1,6 +1,9 @@ // Copyright 2014 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + #include #include "Common/BitSet.h" @@ -29,23 +32,39 @@ TEST(BitSet, BitGetSet) TEST(BitSet, Count) { - u32 random_numbers[] = {0x2cb0b5f3, 0x81ab32a6, 0xd9030dc5, 0x325ffe26, 0xb2fcaee3, - 0x4ccf188a, 0xf8be36dc, 0xb2fcecd5, 0xb750c2e5, 0x31d19074, - 0xf267644a, 0xac00a719, 0x6d45f19b, 0xf7e91c5b, 0xf687e694, - 0x9057c24e, 0x5eb65c39, 0x85d3038b, 0x101f4e66, 0xc202d136}; - u32 counts[] = {17, 14, 14, 19, 20, 14, 20, 20, 16, 13, 16, 12, 18, 20, 18, 14, 18, 14, 14, 12}; - for (size_t i = 0; i < 20; i++) + constexpr std::array, 20> random_32bit_number_bitcount_pairs = { + {{0x2cb0b5f3, 17}, {0x81ab32a6, 14}, {0xd9030dc5, 14}, {0x325ffe26, 19}, {0xb2fcaee3, 20}, + {0x4ccf188a, 14}, {0xf8be36dc, 20}, {0xb2fcecd5, 20}, {0xb750c2e5, 16}, {0x31d19074, 13}, + {0xf267644a, 16}, {0xac00a719, 12}, {0x6d45f19b, 18}, {0xf7e91c5b, 20}, {0xf687e694, 18}, + {0x9057c24e, 14}, {0x5eb65c39, 18}, {0x85d3038b, 14}, {0x101f4e66, 14}, {0xc202d136, 12}}}; + for (const auto& [number, bitcount] : random_32bit_number_bitcount_pairs) { - EXPECT_EQ(counts[i], BitSet32(random_numbers[i]).Count()); + const auto bitset = BitSet32(number); + EXPECT_EQ(bitset.Count(), bitcount); + u32 iterating_count = 0; + for (auto iter = bitset.begin(); iter != bitset.end(); ++iter) + ++iterating_count; + EXPECT_EQ(iterating_count, bitcount); } - u64 random_numbers_64[] = {0xf86cd6f6ef09d7d4ULL, 0x6f2d8533255ead3cULL, 0x9da7941e0e52b345ULL, - 0x06e4189be67d2b17ULL, 0x3eb0681f65cb6d25ULL, 0xccab8a7c74a51203ULL, - 0x09d470516694c64bULL, 0x38cd077e075c778fULL, 0xd69ebfa6355ebfdeULL}; - u32 counts_64[] = {39, 34, 31, 32, 33, 29, 27, 35, 43}; - for (size_t i = 0; i < 9; i++) + constexpr std::array, 9> random_64bit_number_bitcount_pairs = { + {{0xf86cd6f6ef09d7d4ULL, 39}, + {0x6f2d8533255ead3cULL, 34}, + {0x9da7941e0e52b345ULL, 31}, + {0x06e4189be67d2b17ULL, 32}, + {0x3eb0681f65cb6d25ULL, 33}, + {0xccab8a7c74a51203ULL, 29}, + {0x09d470516694c64bULL, 27}, + {0x38cd077e075c778fULL, 35}, + {0xd69ebfa6355ebfdeULL, 43}}}; + for (const auto& [number, bitcount] : random_64bit_number_bitcount_pairs) { - EXPECT_EQ(counts_64[i], BitSet64(random_numbers_64[i]).Count()); + const auto bitset = BitSet64(number); + EXPECT_EQ(bitset.Count(), bitcount); + u32 iterating_count = 0; + for (auto iter = bitset.begin(); iter != bitset.end(); ++iter) + ++iterating_count; + EXPECT_EQ(iterating_count, bitcount); } }