Merge pull request #23 from dolphin-emu/master

[pull] master from dolphin-emu:master
This commit is contained in:
Nayla 2024-01-02 19:47:09 -05:00 committed by GitHub
commit 4d48aed93d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 298 additions and 112 deletions

View file

@ -173,7 +173,7 @@ float4 SharpBilinearSample(float3 uvw, float gamma)
float2 texel = uvw.xy * source_size; float2 texel = uvw.xy * source_size;
float2 texel_floored = floor(texel); float2 texel_floored = floor(texel);
float2 s = fract(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); float region_range = 0.5 - (0.5 / scale);
// Figure out where in the texel to sample to get correct pre-scaled bilinear. // Figure out where in the texel to sample to get correct pre-scaled bilinear.

View file

@ -9,8 +9,11 @@
package org.dolphinemu.dolphinemu.features package org.dolphinemu.dolphinemu.features
import android.annotation.TargetApi import android.annotation.TargetApi
import android.content.res.AssetFileDescriptor
import android.database.Cursor import android.database.Cursor
import android.database.MatrixCursor import android.database.MatrixCursor
import android.graphics.Point
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.CancellationSignal import android.os.CancellationSignal
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
@ -27,7 +30,7 @@ class DocumentProvider : DocumentsProvider() {
private var rootDirectory: File? = null private var rootDirectory: File? = null
companion object { companion object {
public const val ROOT_ID = "root" const val ROOT_ID = "root"
private val DEFAULT_ROOT_PROJECTION = arrayOf( private val DEFAULT_ROOT_PROJECTION = arrayOf(
DocumentsContract.Root.COLUMN_ROOT_ID, DocumentsContract.Root.COLUMN_ROOT_ID,
@ -93,6 +96,8 @@ class DocumentProvider : DocumentsProvider() {
appendDocument(file, result) appendDocument(file, result)
} }
} }
result.setNotificationUri(context!!.contentResolver, DocumentsContract.buildChildDocumentsUri(
"${context!!.packageName}.user", parentDocumentId))
return result return result
} }
@ -107,6 +112,16 @@ class DocumentProvider : DocumentsProvider() {
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode)) 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( override fun createDocument(
parentDocumentId: String, parentDocumentId: String,
mimeType: String, mimeType: String,
@ -121,6 +136,7 @@ class DocumentProvider : DocumentsProvider() {
} else { } else {
file.createNewFile() file.createNewFile()
} }
refreshDocument(parentDocumentId)
return pathToDocumentId(file) return pathToDocumentId(file)
} }
@ -128,7 +144,9 @@ class DocumentProvider : DocumentsProvider() {
rootDirectory ?: return rootDirectory ?: return
val file = documentIdToPath(documentId) val file = documentIdToPath(documentId)
val fileParent = file.parentFile
file.deleteRecursively() file.deleteRecursively()
refreshDocument(pathToDocumentId(fileParent!!))
} }
override fun renameDocument(documentId: String, displayName: String): String? { override fun renameDocument(documentId: String, displayName: String): String? {
@ -137,9 +155,19 @@ class DocumentProvider : DocumentsProvider() {
val file = documentIdToPath(documentId) val file = documentIdToPath(documentId)
val dest = findFileNameForNewFile(File(file.parentFile, displayName)) val dest = findFileNameForNewFile(File(file.parentFile, displayName))
file.renameTo(dest) file.renameTo(dest)
refreshDocument(pathToDocumentId(file.parentFile!!))
return pathToDocumentId(dest) 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 override fun isChildDocument(parentDocumentId: String, documentId: String): Boolean
= documentId.startsWith(parentDocumentId) = documentId.startsWith(parentDocumentId)
@ -161,6 +189,10 @@ class DocumentProvider : DocumentsProvider() {
} else { } else {
file.name file.name
} }
val mimeType = getTypeForFile(file)
if (file.exists() && mimeType.startsWith("image/")) {
flags = flags or DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL
}
cursor.newRow().apply { cursor.newRow().apply {
add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, pathToDocumentId(file)) add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, pathToDocumentId(file))
add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(file)) add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(file))

View file

@ -73,7 +73,7 @@ public:
else else
{ {
int bit = std::countr_zero(m_val); int bit = std::countr_zero(m_val);
m_val &= ~(1 << bit); m_val &= ~(IntTy{1} << bit);
m_bit = bit; m_bit = bit;
} }
return *this; return *this;
@ -98,15 +98,15 @@ public:
constexpr BitSet(std::initializer_list<int> init) constexpr BitSet(std::initializer_list<int> init)
{ {
for (int bit : init) for (int bit : init)
m_val |= (IntTy)1 << bit; m_val |= IntTy{1} << bit;
} }
constexpr static BitSet AllTrue(size_t count) 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<BitSet*>(this))[bit]; } constexpr const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(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; }
constexpr bool operator!=(BitSet other) const { return m_val != other.m_val; } constexpr bool operator!=(BitSet other) const { return m_val != other.m_val; }

View file

@ -10,7 +10,10 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#define strerror_r(err, buf, len) strerror_s(buf, len, err) #define strerror_r(err, buf, len) strerror_s(buf, len, err)
#include "Common/StringUtil.h"
#endif #endif
namespace Common namespace Common
@ -59,11 +62,11 @@ std::string GetLastErrorString()
// Like GetLastErrorString() but if you have already queried the error code. // Like GetLastErrorString() but if you have already queried the error code.
std::string GetWin32ErrorString(DWORD 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); 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. // Obtains a full path to the specified module.

View file

@ -426,7 +426,7 @@ size_t StringUTF8CodePointCount(std::string_view str)
#ifdef _WIN32 #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 = auto const size =
MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
@ -444,7 +444,7 @@ std::wstring CPToUTF16(u32 code_page, std::string_view input)
return output; 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()) if (input.empty())
return {}; return {};

View file

@ -950,6 +950,7 @@ struct ReverbPB
// Base address of the circular buffer in MRAM. // Base address of the circular buffer in MRAM.
u16 circular_buffer_base_h; u16 circular_buffer_base_h;
u16 circular_buffer_base_l; u16 circular_buffer_base_l;
DEFINE_32BIT_ACCESSOR(circular_buffer_base, CircularBufferBase)
struct Destination struct Destination
{ {
@ -990,7 +991,7 @@ void ZeldaAudioRenderer::PrepareFrame()
0xB820); 0xB820);
AddBuffersWithVolume(m_buf_front_left_reverb.data(), m_buf_back_right_reverb.data() + 0x28, 0x28, AddBuffersWithVolume(m_buf_front_left_reverb.data(), m_buf_back_right_reverb.data() + 0x28, 0x28,
0xB820); 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); 0x7FFF);
m_buf_back_left_reverb.fill(0); m_buf_back_left_reverb.fill(0);
m_buf_back_right_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]; 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) + u32 mram_addr = rpb.GetCircularBufferBase() + mram_buffer_idx * 0x50 * sizeof(s16);
mram_buffer_idx * 0x50 * sizeof(s16);
s16* mram_ptr = (s16*)HLEMemory_Get_Pointer(mram_addr); s16* mram_ptr = (s16*)HLEMemory_Get_Pointer(mram_addr);
if (!post_rendering) if (!post_rendering)
@ -1217,11 +1217,9 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id)
// Compute reverb volume and ramp deltas. // Compute reverb volume and ramp deltas.
s16 reverb_volumes[4], reverb_volume_deltas[4]; 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) 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; reverb_volume_deltas[i] = (volume_deltas[i] * vpb.dolby_reverb_factor) >> shift_factor;
} }

View file

@ -577,8 +577,11 @@ void EmulationKernel::AddStaticDevices()
} }
if (HasFeature(features, Feature::KD)) if (HasFeature(features, Feature::KD))
{ {
AddDevice(std::make_unique<NetKDRequestDevice>(*this, "/dev/net/kd/request")); constexpr auto time_device_name = "/dev/net/kd/time";
AddDevice(std::make_unique<NetKDTimeDevice>(*this, "/dev/net/kd/time")); AddDevice(std::make_unique<NetKDTimeDevice>(*this, time_device_name));
const auto time_device =
std::static_pointer_cast<NetKDTimeDevice>(GetDeviceByName(time_device_name));
AddDevice(std::make_unique<NetKDRequestDevice>(*this, "/dev/net/kd/request", time_device));
} }
if (HasFeature(features, Feature::NCD)) if (HasFeature(features, Feature::NCD))
{ {

View file

@ -153,9 +153,10 @@ s32 NWC24MakeUserID(u64* nwc24_id, u32 hollywood_id, u16 id_ctr, HardwareModel h
} }
} // Anonymous namespace } // Anonymous namespace
NetKDRequestDevice::NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name) NetKDRequestDevice::NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name,
const std::shared_ptr<NetKDTimeDevice>& time_device)
: EmulationDevice(ios, device_name), m_config{ios.GetFS()}, m_dl_list{ios.GetFS()}, : 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 // Enable all NWC24 permissions
m_scheduler_buffer[1] = Common::swap32(-1); m_scheduler_buffer[1] = Common::swap32(-1);
@ -190,6 +191,8 @@ NetKDRequestDevice::~NetKDRequestDevice()
} }
m_scheduler_timer_thread.join(); m_scheduler_timer_thread.join();
m_scheduler_work_queue.Shutdown();
m_work_queue.Shutdown();
} }
void NetKDRequestDevice::Update() 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. // 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. // A correct entry is one that hasn't been downloaded the longest compared to other entries.
// We first need current UTC. // We first need current UTC.
const auto time_device = const u64 current_utc = m_time_device->GetAdjustedUTC();
std::static_pointer_cast<NetKDTimeDevice>(GetIOS()->GetDeviceByName("/dev/net/kd/time"));
const u64 current_utc = time_device->GetAdjustedUTC();
u64 lowest_timestamp = std::numeric_limits<u64>::max(); u64 lowest_timestamp = std::numeric_limits<u64>::max();
for (u16 i = 0; i < static_cast<u16>(NWC24::NWC24Dl::MAX_ENTRIES); i++) for (u16 i = 0; i < static_cast<u16>(NWC24::NWC24Dl::MAX_ENTRIES); i++)
@ -493,9 +494,7 @@ NWC24::ErrorCode NetKDRequestDevice::DetermineSubtask(u16 entry_index,
if (m_dl_list.IsSubtaskDownloadDisabled(entry_index)) if (m_dl_list.IsSubtaskDownloadDisabled(entry_index))
return NWC24::WC24_ERR_DISABLED; return NWC24::WC24_ERR_DISABLED;
const auto time_device = const u64 current_utc = m_time_device->GetAdjustedUTC();
std::static_pointer_cast<NetKDTimeDevice>(GetIOS()->GetDeviceByName("/dev/net/kd/time"));
const u64 current_utc = time_device->GetAdjustedUTC();
for (u8 i = 0; i < 32; i++) for (u8 i = 0; i < 32; i++)
{ {
if (!m_dl_list.IsValidSubtask(entry_index, i)) if (!m_dl_list.IsValidSubtask(entry_index, i))
@ -645,9 +644,7 @@ NWC24::ErrorCode NetKDRequestDevice::KDDownload(const u16 entry_index,
{ {
bool success = false; bool success = false;
Common::ScopeGuard state_guard([&] { Common::ScopeGuard state_guard([&] {
const auto time_device = const u64 current_utc = m_time_device->GetAdjustedUTC();
std::static_pointer_cast<NetKDTimeDevice>(GetIOS()->GetDeviceByName("/dev/net/kd/time"));
const u64 current_utc = time_device->GetAdjustedUTC();
if (success) if (success)
{ {
// Set the next download time to the dl_margin // Set the next download time to the dl_margin

View file

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <memory>
#include <queue> #include <queue>
#include <string> #include <string>
@ -17,6 +18,7 @@
#include "Core/IOS/Network/KD/Mail/WC24Send.h" #include "Core/IOS/Network/KD/Mail/WC24Send.h"
#include "Core/IOS/Network/KD/NWC24Config.h" #include "Core/IOS/Network/KD/NWC24Config.h"
#include "Core/IOS/Network/KD/NWC24DL.h" #include "Core/IOS/Network/KD/NWC24DL.h"
#include "Core/IOS/Network/KD/NetKDTime.h"
namespace IOS::HLE namespace IOS::HLE
{ {
@ -26,7 +28,8 @@ namespace IOS::HLE
class NetKDRequestDevice : public EmulationDevice class NetKDRequestDevice : public EmulationDevice
{ {
public: public:
NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name); NetKDRequestDevice(EmulationKernel& ios, const std::string& device_name,
const std::shared_ptr<NetKDTimeDevice>& time_device);
IPCReply HandleNWC24DownloadNowEx(const IOCtlRequest& request); IPCReply HandleNWC24DownloadNowEx(const IOCtlRequest& request);
NWC24::ErrorCode KDDownload(const u16 entry_index, const std::optional<u8> subtask_id); NWC24::ErrorCode KDDownload(const u16 entry_index, const std::optional<u8> subtask_id);
IPCReply HandleNWC24CheckMailNow(const IOCtlRequest& request); IPCReply HandleNWC24CheckMailNow(const IOCtlRequest& request);
@ -114,6 +117,7 @@ private:
std::queue<AsyncReply> m_async_replies; std::queue<AsyncReply> m_async_replies;
u32 m_error_count = 0; u32 m_error_count = 0;
std::array<u32, 256> m_scheduler_buffer{}; std::array<u32, 256> m_scheduler_buffer{};
std::shared_ptr<NetKDTimeDevice> m_time_device;
// TODO: Maybe move away from Common::HttpRequest? // TODO: Maybe move away from Common::HttpRequest?
Common::HttpRequest m_http{std::chrono::minutes{1}}; Common::HttpRequest m_http{std::chrono::minutes{1}};
u32 m_download_span = 2; u32 m_download_span = 2;

View file

@ -221,6 +221,9 @@ void Jit64AsmRoutineManager::Generate()
ABI_CallFunction(JitTrampoline); ABI_CallFunction(JitTrampoline);
ABI_PopRegistersAndAdjustStack({}, 0); 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); JMP(dispatcher_no_check, Jump::Near);
SetJumpTarget(bail); SetJumpTarget(bail);

View file

@ -939,7 +939,6 @@ void JitArm64::dcbz(UGeckoInstruction inst)
{ {
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITLoadStoreOff); JITDISABLE(bJITLoadStoreOff);
FALLBACK_IF(m_low_dcbz_hack);
int a = inst.RA, b = inst.RB; int a = inst.RA, b = inst.RB;

View file

@ -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) 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, ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded,
"Attempted to flush discarded register"); "Attempted to flush discarded register");
@ -269,7 +270,7 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, bool maintain_state, ARM64Reg
reg1.Flush(); reg1.Flush();
reg2.Flush(); reg2.Flush();
} }
++i; ++iter;
continue; continue;
} }
} }

View file

@ -177,7 +177,12 @@ void JitArm64::GenerateAsm()
// Call JIT // Call JIT
ResetStack(); ResetStack();
ABI_CallFunction(&JitTrampoline, this, DISPATCHER_PC); ABI_CallFunction(&JitTrampoline, this, DISPATCHER_PC);
LDR(IndexType::Unsigned, DISPATCHER_PC, PPC_REG, PPCSTATE_OFF(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); B(dispatcher_no_check);
SetJumpTarget(bail); SetJumpTarget(bail);

View file

@ -1343,7 +1343,8 @@ static TLBLookupResult LookupTLBPageAddress(PowerPC::PowerPCState& ppc_state,
u32* paddr, bool* wi) u32* paddr, bool* wi)
{ {
const u32 tag = vpa >> HW_PAGE_INDEX_SHIFT; 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) if (tlbe.tag[0] == tag && tlbe.vsid[0] == vsid)
{ {
@ -1401,7 +1402,8 @@ static void UpdateTLBEntry(PowerPC::PowerPCState& ppc_state, const XCheckTLBFlag
return; return;
const u32 tag = address >> HW_PAGE_INDEX_SHIFT; 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; const u32 index = tlbe.recent == 0 && tlbe.tag[0] != TLBEntry::INVALID_TAG;
tlbe.recent = index; tlbe.recent = index;
tlbe.paddr[index] = pte2.RPN << HW_PAGE_INDEX_SHIFT; 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; 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[PowerPC::DATA_TLB_INDEX][entry_index].Invalidate();
m_ppc_state.tlb[1][entry_index].Invalidate(); m_ppc_state.tlb[PowerPC::INST_TLB_INDEX][entry_index].Invalidate();
} }
// Page Address Translation // Page Address Translation

View file

@ -53,6 +53,8 @@ enum class CoreMode
constexpr size_t TLB_SIZE = 128; constexpr size_t TLB_SIZE = 128;
constexpr size_t NUM_TLBS = 2; constexpr size_t NUM_TLBS = 2;
constexpr size_t TLB_WAYS = 2; constexpr size_t TLB_WAYS = 2;
constexpr size_t DATA_TLB_INDEX = 0;
constexpr size_t INST_TLB_INDEX = 1;
struct TLBEntry struct TLBEntry
{ {

View file

@ -184,7 +184,10 @@ void GeneralWidget::SaveSettings()
{ {
// Video Backend // Video Backend
const auto current_backend = m_backend_combo->currentData().toString().toStdString(); 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()] auto warningMessage = VideoBackendBase::GetAvailableBackends()[m_backend_combo->currentIndex()]
->GetWarningMessage(); ->GetWarningMessage();
@ -205,8 +208,10 @@ void GeneralWidget::SaveSettings()
return; 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) void GeneralWidget::OnEmulationStateChanged(bool running)
@ -217,6 +222,10 @@ void GeneralWidget::OnEmulationStateChanged(bool running)
const bool supports_adapters = !g_Config.backend_info.Adapters.empty(); const bool supports_adapters = !g_Config.backend_info.Adapters.empty();
m_adapter_combo->setEnabled(!running && supports_adapters); 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() void GeneralWidget::AddDescriptions()

View file

@ -67,7 +67,6 @@ void GraphicsWindow::CreateMainLayout()
void GraphicsWindow::OnBackendChanged(const QString& backend_name) void GraphicsWindow::OnBackendChanged(const QString& backend_name)
{ {
Config::SetBase(Config::MAIN_GFX_BACKEND, backend_name.toStdString());
VideoBackendBase::PopulateBackendInfoFromUI(m_main_window->GetWindowSystemInfo()); VideoBackendBase::PopulateBackendInfoFromUI(m_main_window->GetWindowSystemInfo());
setWindowTitle( setWindowTitle(

View file

@ -3,6 +3,8 @@
#include "DolphinQt/Config/Mapping/MappingWidget.h" #include "DolphinQt/Config/Mapping/MappingWidget.h"
#include <fmt/core.h>
#include <QCheckBox> #include <QCheckBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QFormLayout> #include <QFormLayout>
@ -25,6 +27,7 @@
#include "InputCommon/ControllerEmu/ControllerEmu.h" #include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h" #include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
#include "InputCommon/ControllerEmu/StickGate.h" #include "InputCommon/ControllerEmu/StickGate.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
MappingWidget::MappingWidget(MappingWindow* parent) : m_parent(parent) MappingWidget::MappingWidget(MappingWindow* parent) : m_parent(parent)
{ {
@ -160,6 +163,26 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
[this, group] { ShowAdvancedControlGroupDialog(group); }); [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; return group_box;
} }

View file

@ -45,6 +45,8 @@ ControllerInterface g_controller_interface;
// will never interfere with game threads. // will never interfere with game threads.
static thread_local ciface::InputChannel tls_input_channel = ciface::InputChannel::Host; 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) void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
{ {
if (m_is_init) if (m_is_init)
@ -122,8 +124,8 @@ void ControllerInterface::RefreshDevices(RefreshReason reason)
// We lock m_devices_population_mutex here to make everything simpler. // 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 // 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 // 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 // means a thread could be adding devices while we are removing them from a different thread,
// populating them (causing missing or duplicate devices). // or removing them as we are populating them (causing missing or duplicate devices).
std::lock_guard lk_population(m_devices_population_mutex); std::lock_guard lk_population(m_devices_population_mutex);
#if defined(CIFACE_USE_WIN32) && !defined(CIFACE_USE_XLIB) && !defined(CIFACE_USE_OSX) #if defined(CIFACE_USE_WIN32) && !defined(CIFACE_USE_XLIB) && !defined(CIFACE_USE_OSX)
@ -271,6 +273,10 @@ bool ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device
if (!m_is_init) if (!m_is_init)
return false; 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); std::lock_guard lk_population(m_devices_population_mutex);
{ {
@ -328,6 +334,10 @@ void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::De
if (!m_is_init) if (!m_is_init)
return; return;
ASSERT_MSG(CONTROLLERINTERFACE, !tls_is_updating_devices,
"Devices shouldn't be removed 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); std::lock_guard lk_population(m_devices_population_mutex);
bool any_removed; bool any_removed;
@ -358,29 +368,48 @@ void ControllerInterface::UpdateInput()
if (!m_is_init) if (!m_is_init)
return; return;
// TODO: if we are an emulation input channel, we should probably always lock // We add the devices to remove while we still have the "m_devices_mutex" locked.
// Prefer outdated values over blocking UI or CPU thread (avoids short but noticeable frame drop) // This guarantees that:
// -We won't try to lock "m_devices_population_mutex" while it was already locked and waiting
// Lock this first to avoid deadlock with m_devices_mutex in certain cases (such as a Wii Remote // for "m_devices_mutex", which would result in dead lock.
// getting disconnected) // -We don't keep shared ptrs on devices and thus unwillingly keep them alive even if somebody
if (!m_devices_population_mutex.try_lock()) // is currently trying to remove them (and needs them destroyed on the spot).
return; // -If somebody else destroyed them in the meantime, we'll know which ones have been destroyed.
std::vector<std::weak_ptr<ciface::Core::Device>> devices_to_remove;
std::lock_guard population_lock(m_devices_population_mutex, std::adopt_lock);
{
// 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()) if (!m_devices_mutex.try_lock())
return; return;
std::lock_guard lk(m_devices_mutex, std::adopt_lock); std::lock_guard lk_devices(m_devices_mutex, std::adopt_lock);
tls_is_updating_devices = true;
for (auto& backend : m_input_backends) for (auto& backend : m_input_backends)
backend->UpdateInput(); backend->UpdateInput(devices_to_remove);
for (const auto& d : m_devices) for (const auto& d : m_devices)
{ {
// Theoretically we could avoid updating input on devices that don't have any references to // 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 // them, but in practice a few devices types could break in different ways, so we don't
d->UpdateInput(); 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<ciface::Core::Device>& d) {
return d.lock().get() == device;
});
});
} }
} }

View file

@ -127,7 +127,6 @@ private:
std::list<std::function<void()>> m_devices_changed_callbacks; std::list<std::function<void()>> m_devices_changed_callbacks;
mutable std::recursive_mutex m_devices_population_mutex; mutable std::recursive_mutex m_devices_population_mutex;
mutable std::mutex m_pre_population_mutex;
mutable std::mutex m_callbacks_mutex; mutable std::mutex m_callbacks_mutex;
std::atomic<bool> m_is_init; std::atomic<bool> m_is_init;
// This is now always protected by m_devices_population_mutex, so // This is now always protected by m_devices_population_mutex, so

View file

@ -36,6 +36,12 @@ constexpr ControlState BATTERY_INPUT_MAX_VALUE = 100.0;
namespace Core namespace Core
{ {
enum class DeviceRemoval
{
Remove,
Keep,
};
class Device class Device
{ {
public: public:
@ -118,7 +124,7 @@ public:
virtual std::string GetName() const = 0; virtual std::string GetName() const = 0;
virtual std::string GetSource() const = 0; virtual std::string GetSource() const = 0;
std::string GetQualifiedName() const; std::string GetQualifiedName() const;
virtual void UpdateInput() {} virtual DeviceRemoval UpdateInput() { return DeviceRemoval::Keep; }
// May be overridden to implement hotplug removal. // May be overridden to implement hotplug removal.
// Currently handled on a per-backend basis but this could change. // 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; } std::recursive_mutex& GetDevicesMutex() const { return m_devices_mutex; }
protected: 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; mutable std::recursive_mutex m_devices_mutex;
std::vector<std::shared_ptr<Device>> m_devices; std::vector<std::shared_ptr<Device>> m_devices;
}; };

View file

@ -222,7 +222,7 @@ bool Joystick::IsValid() const
return SUCCEEDED(m_device->Acquire()); return SUCCEEDED(m_device->Acquire());
} }
void Joystick::UpdateInput() Core::DeviceRemoval Joystick::UpdateInput()
{ {
HRESULT hr = 0; HRESULT hr = 0;
@ -261,6 +261,8 @@ void Joystick::UpdateInput()
// try reacquire if input lost // try reacquire if input lost
if (DIERR_INPUTLOST == hr || DIERR_NOTACQUIRED == hr) if (DIERR_INPUTLOST == hr || DIERR_NOTACQUIRED == hr)
m_device->Acquire(); m_device->Acquire();
return Core::DeviceRemoval::Keep;
} }
// get name // get name

View file

@ -57,7 +57,7 @@ private:
}; };
public: public:
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
Joystick(const LPDIRECTINPUTDEVICE8 device); Joystick(const LPDIRECTINPUTDEVICE8 device);
~Joystick(); ~Joystick();

View file

@ -205,7 +205,7 @@ void KeyboardMouse::UpdateCursorInput()
m_state_in.cursor.y = (ControlState(point.y) / win_height * 2 - 1) * window_scale.y; m_state_in.cursor.y = (ControlState(point.y) / win_height * 2 - 1) * window_scale.y;
} }
void KeyboardMouse::UpdateInput() Core::DeviceRemoval KeyboardMouse::UpdateInput()
{ {
UpdateCursorInput(); UpdateCursorInput();
@ -254,6 +254,8 @@ void KeyboardMouse::UpdateInput()
else else
INFO_LOG_FMT(CONTROLLERINTERFACE, "Keyboard device failed to re-acquire, we'll retry later"); INFO_LOG_FMT(CONTROLLERINTERFACE, "Keyboard device failed to re-acquire, we'll retry later");
} }
return Core::DeviceRemoval::Keep;
} }
std::string KeyboardMouse::GetName() const std::string KeyboardMouse::GetName() const

View file

@ -94,7 +94,7 @@ private:
}; };
public: public:
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device); KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device);
~KeyboardMouse(); ~KeyboardMouse();

View file

@ -128,7 +128,7 @@ private:
}; };
public: public:
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
Device(std::string name, int index, std::string server_address, u16 server_port, u32 client_uid); 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); return std::string(DUALSHOCKUDP_SOURCE_NAME);
} }
void Device::UpdateInput() Core::DeviceRemoval Device::UpdateInput()
{ {
// Regularly tell the UDP server to feed us controller data // Regularly tell the UDP server to feed us controller data
const auto now = SteadyClock::now(); const auto now = SteadyClock::now();
@ -660,6 +660,8 @@ void Device::UpdateInput()
m_prev_touch_valid = true; m_prev_touch_valid = true;
} }
} }
return Core::DeviceRemoval::Keep;
} }
std::optional<int> Device::GetPreferredId() const std::optional<int> Device::GetPreferredId() const

View file

@ -12,7 +12,7 @@ InputBackend::InputBackend(ControllerInterface* controller_interface)
InputBackend::~InputBackend() = default; InputBackend::~InputBackend() = default;
void InputBackend::UpdateInput() void InputBackend::UpdateInput(std::vector<std::weak_ptr<ciface::Core::Device>>& devices_to_remove)
{ {
} }

View file

@ -3,10 +3,19 @@
#pragma once #pragma once
#include <memory>
#include <vector>
class ControllerInterface; class ControllerInterface;
namespace ciface namespace ciface
{ {
namespace Core
{
class Device;
}
class InputBackend class InputBackend
{ {
public: public:
@ -15,7 +24,9 @@ public:
virtual ~InputBackend(); virtual ~InputBackend();
virtual void PopulateDevices() = 0; 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<std::weak_ptr<ciface::Core::Device>>& devices_to_remove);
ControllerInterface& GetControllerInterface(); ControllerInterface& GetControllerInterface();

View file

@ -86,7 +86,7 @@ PipeDevice::~PipeDevice()
close(m_fd); close(m_fd);
} }
void PipeDevice::UpdateInput() Core::DeviceRemoval PipeDevice::UpdateInput()
{ {
// Read any pending characters off the pipe. If we hit a newline, // 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. // 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); m_buf.erase(0, newline + 1);
newline = m_buf.find("\n"); newline = m_buf.find("\n");
} }
return Core::DeviceRemoval::Keep;
} }
void PipeDevice::AddAxis(const std::string& name, double value) void PipeDevice::AddAxis(const std::string& name, double value)

View file

@ -29,7 +29,7 @@ public:
PipeDevice(int fd, const std::string& name); PipeDevice(int fd, const std::string& name);
~PipeDevice(); ~PipeDevice();
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
std::string GetName() const override { return m_name; } std::string GetName() const override { return m_name; }
std::string GetSource() const override { return "Pipe"; } std::string GetSource() const override { return "Pipe"; }

View file

@ -62,7 +62,7 @@ private:
}; };
public: public:
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
explicit KeyboardAndMouse(void* view); explicit KeyboardAndMouse(void* view);
~KeyboardAndMouse() override; ~KeyboardAndMouse() override;

View file

@ -236,7 +236,7 @@ void KeyboardAndMouse::MainThreadInitialization(void* view)
m_window_pos_observer = [[DolWindowPositionObserver alloc] initWithView:cocoa_view]; m_window_pos_observer = [[DolWindowPositionObserver alloc] initWithView:cocoa_view];
} }
void KeyboardAndMouse::UpdateInput() Core::DeviceRemoval KeyboardAndMouse::UpdateInput()
{ {
NSRect bounds = [m_window_pos_observer frame]; 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.x = (loc.x / window_width * 2 - 1.0) * window_scale.x;
m_cursor.y = (loc.y / window_height * 2 - 1.0) * -window_scale.y; m_cursor.y = (loc.y / window_height * 2 - 1.0) * -window_scale.y;
} }
return Core::DeviceRemoval::Keep;
} }
std::string KeyboardAndMouse::GetName() const std::string KeyboardAndMouse::GetName() const

View file

@ -18,6 +18,11 @@
#include <Windows.h> #include <Windows.h>
#endif #endif
namespace ciface::Core
{
class Device;
}
namespace ciface::SDL namespace ciface::SDL
{ {
static std::string GetJoystickName(int index) static std::string GetJoystickName(int index)
@ -35,7 +40,7 @@ public:
InputBackend(ControllerInterface* controller_interface); InputBackend(ControllerInterface* controller_interface);
~InputBackend(); ~InputBackend();
void PopulateDevices() override; void PopulateDevices() override;
void UpdateInput() override; void UpdateInput(std::vector<std::weak_ptr<ciface::Core::Device>>& devices_to_remove) override;
private: private:
void OpenAndAddDevice(int index); void OpenAndAddDevice(int index);
@ -637,7 +642,7 @@ void Joystick::Motor::SetState(ControlState state)
} }
#endif #endif
void InputBackend::UpdateInput() void InputBackend::UpdateInput(std::vector<std::weak_ptr<ciface::Core::Device>>& devices_to_remove)
{ {
SDL_JoystickUpdate(); SDL_JoystickUpdate();
} }

View file

@ -113,7 +113,7 @@ public:
Device(hid_device* device); Device(hid_device* device);
std::string GetName() const final override; std::string GetName() const final override;
std::string GetSource() const final override; std::string GetSource() const final override;
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
private: private:
hid_device* m_device; hid_device* m_device;
@ -279,7 +279,7 @@ std::string Device::GetSource() const
return std::string(STEAMDECK_SOURCE_NAME); return std::string(STEAMDECK_SOURCE_NAME);
} }
void Device::UpdateInput() Core::DeviceRemoval Device::UpdateInput()
{ {
// As of a certain mid-2023 update to the Steam client, // As of a certain mid-2023 update to the Steam client,
// Steam will disable gyro data if gyro is not mapped in Steam Input. // 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. // In case there were no reports available to be read, bail early.
if (!got_anything) if (!got_anything)
return; return Core::DeviceRemoval::Keep;
if (rpt.major_ver != 0x01 || rpt.minor_ver != 0x00 || rpt.report_type != 0x09 || if (rpt.major_ver != 0x01 || rpt.minor_ver != 0x00 || rpt.report_type != 0x09 ||
rpt.report_sz != sizeof(rpt)) rpt.report_sz != sizeof(rpt))
{ {
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Steam Deck bad report"); ERROR_LOG_FMT(CONTROLLERINTERFACE, "Steam Deck bad report");
return; return Core::DeviceRemoval::Keep;
} }
m_latest_input = rpt; m_latest_input = rpt;
return Core::DeviceRemoval::Keep;
} }
} // namespace ciface::SteamDeck } // namespace ciface::SteamDeck

View file

@ -490,7 +490,7 @@ private:
std::string GetSource() const override { return std::string(SOURCE_NAME); } std::string GetSource() const override { return std::string(SOURCE_NAME); }
void UpdateInput() override Core::DeviceRemoval UpdateInput() override
{ {
// IRawGameController: // IRawGameController:
static_assert(sizeof(bool) == sizeof(ButtonValueType)); static_assert(sizeof(bool) == sizeof(ButtonValueType));
@ -527,6 +527,8 @@ private:
// IGameControllerBatteryInfo: // IGameControllerBatteryInfo:
if (!UpdateBatteryLevel()) if (!UpdateBatteryLevel())
DEBUG_LOG_FMT(CONTROLLERINTERFACE, "WGInput: UpdateBatteryLevel failed."); DEBUG_LOG_FMT(CONTROLLERINTERFACE, "WGInput: UpdateBatteryLevel failed.");
return Core::DeviceRemoval::Keep;
} }
void UpdateMotors() void UpdateMotors()

View file

@ -1395,14 +1395,10 @@ void Device::UpdateRumble()
QueueReport(OutputReportRumble{}); QueueReport(OutputReportRumble{});
} }
void Device::UpdateInput() Core::DeviceRemoval Device::UpdateInput()
{ {
if (!m_wiimote->IsConnected()) if (!m_wiimote->IsConnected())
{ return Core::DeviceRemoval::Remove;
g_controller_interface.RemoveDevice(
[this](const Core::Device* device) { return device == this; });
return;
}
UpdateRumble(); UpdateRumble();
RunTasks(); RunTasks();
@ -1413,6 +1409,8 @@ void Device::UpdateInput()
ProcessInputReport(report); ProcessInputReport(report);
RunTasks(); RunTasks();
} }
return Core::DeviceRemoval::Keep;
} }
void Device::MotionPlusState::ProcessData(const WiimoteEmu::MotionPlus::DataFormat& data) void Device::MotionPlusState::ProcessData(const WiimoteEmu::MotionPlus::DataFormat& data)

View file

@ -35,7 +35,7 @@ public:
std::string GetSource() const override; std::string GetSource() const override;
int GetSortPriority() const override; int GetSortPriority() const override;
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
private: private:
using Clock = std::chrono::steady_clock; using Clock = std::chrono::steady_clock;

View file

@ -264,7 +264,7 @@ std::string Device::GetSource() const
return "XInput"; return "XInput";
} }
void Device::UpdateInput() Core::DeviceRemoval Device::UpdateInput()
{ {
PXInputGetState(m_index, &m_state_in); PXInputGetState(m_index, &m_state_in);
@ -286,6 +286,8 @@ void Device::UpdateInput()
break; break;
} }
} }
return Core::DeviceRemoval::Keep;
} }
void Device::UpdateMotors() void Device::UpdateMotors()

View file

@ -33,7 +33,7 @@ public:
std::optional<int> GetPreferredId() const override; std::optional<int> GetPreferredId() const override;
int GetSortPriority() const override { return -2; } int GetSortPriority() const override { return -2; }
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
void UpdateMotors(); void UpdateMotors();

View file

@ -275,7 +275,7 @@ void KeyboardMouse::UpdateCursor(bool should_center_mouse)
m_state.cursor.y = (win_y / win_height * 2 - 1) * window_scale.y; m_state.cursor.y = (win_y / win_height * 2 - 1) * window_scale.y;
} }
void KeyboardMouse::UpdateInput() Core::DeviceRemoval KeyboardMouse::UpdateInput()
{ {
XFlush(m_display); XFlush(m_display);
@ -369,6 +369,8 @@ void KeyboardMouse::UpdateInput()
if (update_keyboard) if (update_keyboard)
XQueryKeymap(m_display, m_state.keyboard.data()); XQueryKeymap(m_display, m_state.keyboard.data());
return Core::DeviceRemoval::Keep;
} }
std::string KeyboardMouse::GetName() const std::string KeyboardMouse::GetName() const

View file

@ -111,7 +111,7 @@ private:
void UpdateCursor(bool should_center_mouse); void UpdateCursor(bool should_center_mouse);
public: public:
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid, KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid,
double scroll_increment); double scroll_increment);

View file

@ -674,7 +674,7 @@ void InputBackend::RemoveDevnodeObject(const std::string& node)
m_devnode_objects.erase(node); m_devnode_objects.erase(node);
} }
void evdevDevice::UpdateInput() Core::DeviceRemoval evdevDevice::UpdateInput()
{ {
// Run through all evdev events // Run through all evdev events
// libevdev will keep track of the actual controller state internally which can be queried // 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); rc = libevdev_next_event(node.device, LIBEVDEV_READ_FLAG_NORMAL, &ev);
} }
} }
return Core::DeviceRemoval::Keep;
} }
bool evdevDevice::IsValid() const bool evdevDevice::IsValid() const

View file

@ -72,7 +72,7 @@ private:
}; };
public: public:
void UpdateInput() override; Core::DeviceRemoval UpdateInput() override;
bool IsValid() const override; bool IsValid() const override;
evdevDevice(InputBackend* input_backend); evdevDevice(InputBackend* input_backend);

View file

@ -19,6 +19,26 @@ TCShaderUid GetShaderUid(EFBCopyFormat dst_format, bool is_depth_copy, bool is_i
TCShaderUid out; TCShaderUid out;
UidData* const uid_data = out.GetUidData(); 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->dst_format = dst_format;
uid_data->efb_has_alpha = bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24; uid_data->efb_has_alpha = bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24;
uid_data->is_depth_copy = is_depth_copy; uid_data->is_depth_copy = is_depth_copy;

View file

@ -1,6 +1,9 @@
// Copyright 2014 Dolphin Emulator Project // Copyright 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <utility>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "Common/BitSet.h" #include "Common/BitSet.h"
@ -29,23 +32,39 @@ TEST(BitSet, BitGetSet)
TEST(BitSet, Count) TEST(BitSet, Count)
{ {
u32 random_numbers[] = {0x2cb0b5f3, 0x81ab32a6, 0xd9030dc5, 0x325ffe26, 0xb2fcaee3, constexpr std::array<std::pair<u32, u32>, 20> random_32bit_number_bitcount_pairs = {
0x4ccf188a, 0xf8be36dc, 0xb2fcecd5, 0xb750c2e5, 0x31d19074, {{0x2cb0b5f3, 17}, {0x81ab32a6, 14}, {0xd9030dc5, 14}, {0x325ffe26, 19}, {0xb2fcaee3, 20},
0xf267644a, 0xac00a719, 0x6d45f19b, 0xf7e91c5b, 0xf687e694, {0x4ccf188a, 14}, {0xf8be36dc, 20}, {0xb2fcecd5, 20}, {0xb750c2e5, 16}, {0x31d19074, 13},
0x9057c24e, 0x5eb65c39, 0x85d3038b, 0x101f4e66, 0xc202d136}; {0xf267644a, 16}, {0xac00a719, 12}, {0x6d45f19b, 18}, {0xf7e91c5b, 20}, {0xf687e694, 18},
u32 counts[] = {17, 14, 14, 19, 20, 14, 20, 20, 16, 13, 16, 12, 18, 20, 18, 14, 18, 14, 14, 12}; {0x9057c24e, 14}, {0x5eb65c39, 18}, {0x85d3038b, 14}, {0x101f4e66, 14}, {0xc202d136, 12}}};
for (size_t i = 0; i < 20; i++) 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, constexpr std::array<std::pair<u64, u32>, 9> random_64bit_number_bitcount_pairs = {
0x06e4189be67d2b17ULL, 0x3eb0681f65cb6d25ULL, 0xccab8a7c74a51203ULL, {{0xf86cd6f6ef09d7d4ULL, 39},
0x09d470516694c64bULL, 0x38cd077e075c778fULL, 0xd69ebfa6355ebfdeULL}; {0x6f2d8533255ead3cULL, 34},
u32 counts_64[] = {39, 34, 31, 32, 33, 29, 27, 35, 43}; {0x9da7941e0e52b345ULL, 31},
for (size_t i = 0; i < 9; i++) {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);
} }
} }