mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-12-23 06:57:08 +00:00
Merge a2591fcb4f into ed2fe134aa
This commit is contained in:
commit
31c09b9761
@ -98,6 +98,8 @@ public:
|
|||||||
m_items.WaitForEmpty();
|
m_items.WaitForEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsRunning() { return m_thread.joinable(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using CommandFunction = std::function<void()>;
|
using CommandFunction = std::function<void()>;
|
||||||
|
|
||||||
@ -142,8 +144,6 @@ private:
|
|||||||
return std::lock_guard{m_mutex};
|
return std::lock_guard{m_mutex};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRunning() { return m_thread.joinable(); }
|
|
||||||
|
|
||||||
void ThreadLoop(const std::string& thread_name, const FunctionType& function)
|
void ThreadLoop(const std::string& thread_name, const FunctionType& function)
|
||||||
{
|
{
|
||||||
Common::SetCurrentThreadName(thread_name.c_str());
|
Common::SetCurrentThreadName(thread_name.c_str());
|
||||||
|
|||||||
@ -254,9 +254,8 @@ bool Core::Start(u64 gc_ticks)
|
|||||||
|
|
||||||
if (Config::Get(Config::MAIN_GBA_THREADS))
|
if (Config::Get(Config::MAIN_GBA_THREADS))
|
||||||
{
|
{
|
||||||
m_idle = true;
|
m_event_thread.Reset(fmt::format("GBA{}", m_device_number + 1),
|
||||||
m_exit_loop = false;
|
std::bind_front(&Core::HandleEvent, this));
|
||||||
m_thread = std::make_unique<std::thread>([this] { ThreadLoop(); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -264,17 +263,8 @@ bool Core::Start(u64 gc_ticks)
|
|||||||
|
|
||||||
void Core::Stop()
|
void Core::Stop()
|
||||||
{
|
{
|
||||||
if (m_thread)
|
m_event_thread.Shutdown();
|
||||||
{
|
|
||||||
Flush();
|
|
||||||
m_exit_loop = true;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_queue_mutex);
|
|
||||||
m_command_cv.notify_one();
|
|
||||||
}
|
|
||||||
m_thread->join();
|
|
||||||
m_thread.reset();
|
|
||||||
}
|
|
||||||
if (m_core)
|
if (m_core)
|
||||||
{
|
{
|
||||||
mCoreConfigDeinit(&m_core->config);
|
mCoreConfigDeinit(&m_core->config);
|
||||||
@ -481,103 +471,76 @@ void Core::SetupEvent()
|
|||||||
m_event.priority = 0x80;
|
m_event.priority = 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::SyncJoybus(u64 gc_ticks, u16 keys)
|
||||||
|
{
|
||||||
|
PushEvent({
|
||||||
|
.run_until_ticks = gc_ticks,
|
||||||
|
.keys = keys,
|
||||||
|
.event_type = JoybusEventType::TimeSync,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Core::SendJoybusCommand(u64 gc_ticks, int transfer_time, u8* buffer, u16 keys)
|
void Core::SendJoybusCommand(u64 gc_ticks, int transfer_time, u8* buffer, u16 keys)
|
||||||
{
|
{
|
||||||
if (!IsStarted())
|
if (!IsStarted())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Command command{};
|
m_joybus_command_transfer_time = transfer_time;
|
||||||
command.ticks = gc_ticks;
|
m_joybus_command = GBASIOJOYCommand(buffer[0]);
|
||||||
command.transfer_time = transfer_time;
|
std::copy_n(buffer + 1, m_joybus_buffer.size(), m_joybus_buffer.data());
|
||||||
command.sync_only = buffer == nullptr;
|
|
||||||
if (buffer)
|
|
||||||
std::copy_n(buffer, command.buffer.size(), command.buffer.begin());
|
|
||||||
command.keys = keys;
|
|
||||||
|
|
||||||
if (m_thread)
|
m_command_pending.store(true, std::memory_order_relaxed);
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_queue_mutex);
|
PushEvent({
|
||||||
m_command_queue.push(command);
|
.run_until_ticks = gc_ticks,
|
||||||
m_idle = false;
|
.keys = keys,
|
||||||
m_command_cv.notify_one();
|
.event_type = JoybusEventType::RunCommand,
|
||||||
}
|
});
|
||||||
else
|
|
||||||
{
|
|
||||||
RunCommand(command);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> Core::GetJoybusResponse()
|
int Core::GetJoybusResponse(u8* data_out)
|
||||||
{
|
{
|
||||||
if (!IsStarted())
|
m_command_pending.wait(true, std::memory_order_acquire);
|
||||||
return {};
|
|
||||||
|
|
||||||
if (m_thread)
|
std::copy_n(m_joybus_buffer.data(), m_response_size, data_out);
|
||||||
{
|
return m_response_size;
|
||||||
std::unique_lock<std::mutex> lock(m_response_mutex);
|
|
||||||
m_response_cv.wait(lock, [&] { return m_response_ready; });
|
|
||||||
}
|
|
||||||
m_response_ready = false;
|
|
||||||
return m_response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Flush()
|
void Core::Flush()
|
||||||
{
|
{
|
||||||
if (!IsStarted() || !m_thread)
|
m_event_thread.WaitForCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::PushEvent(JoybusEvent event)
|
||||||
|
{
|
||||||
|
if (m_event_thread.IsRunning())
|
||||||
|
m_event_thread.Push(event);
|
||||||
|
else
|
||||||
|
HandleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::HandleEvent(JoybusEvent event)
|
||||||
|
{
|
||||||
|
m_keys = event.keys;
|
||||||
|
RunUntil(event.run_until_ticks);
|
||||||
|
|
||||||
|
if (event.event_type != JoybusEventType::RunCommand)
|
||||||
return;
|
return;
|
||||||
std::unique_lock<std::mutex> lock(m_queue_mutex);
|
|
||||||
m_response_cv.wait(lock, [&] { return m_idle; });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::ThreadLoop()
|
if (m_link_enabled && !m_force_disconnect)
|
||||||
{
|
|
||||||
Common::SetCurrentThreadName(fmt::format("GBA{}", m_device_number + 1).c_str());
|
|
||||||
std::unique_lock<std::mutex> queue_lock(m_queue_mutex);
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
m_command_cv.wait(queue_lock, [&] { return !m_command_queue.empty() || m_exit_loop; });
|
m_response_size =
|
||||||
if (m_exit_loop)
|
u8(GBASIOJOYSendCommand(&m_sio_driver, m_joybus_command, m_joybus_buffer.data()));
|
||||||
break;
|
|
||||||
Command command{m_command_queue.front()};
|
|
||||||
m_command_queue.pop();
|
|
||||||
queue_lock.unlock();
|
|
||||||
|
|
||||||
RunCommand(command);
|
|
||||||
|
|
||||||
queue_lock.lock();
|
|
||||||
if (m_command_queue.empty())
|
|
||||||
m_idle = true;
|
|
||||||
m_response_cv.notify_one();
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
void Core::RunCommand(Command& command)
|
|
||||||
{
|
|
||||||
m_keys = command.keys;
|
|
||||||
RunUntil(command.ticks);
|
|
||||||
if (!command.sync_only)
|
|
||||||
{
|
{
|
||||||
m_response.clear();
|
m_response_size = 0;
|
||||||
if (m_link_enabled && !m_force_disconnect)
|
|
||||||
{
|
|
||||||
int recvd = GBASIOJOYSendCommand(
|
|
||||||
&m_sio_driver, static_cast<GBASIOJOYCommand>(command.buffer[0]), &command.buffer[1]);
|
|
||||||
std::copy_n(command.buffer.begin() + 1, recvd, std::back_inserter(m_response));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_thread && !m_response_ready)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> response_lock(m_response_mutex);
|
|
||||||
m_response_ready = true;
|
|
||||||
m_response_cv.notify_one();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_response_ready = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (command.transfer_time)
|
|
||||||
RunFor(command.transfer_time);
|
m_command_pending.store(false, std::memory_order_release);
|
||||||
|
m_command_pending.notify_one();
|
||||||
|
|
||||||
|
RunFor(m_joybus_command_transfer_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RunUntil(u64 gc_ticks)
|
void Core::RunUntil(u64 gc_ticks)
|
||||||
@ -700,8 +663,8 @@ void Core::DoState(PointerWrap& p)
|
|||||||
p.Do(m_gc_ticks_remainder);
|
p.Do(m_gc_ticks_remainder);
|
||||||
p.Do(m_keys);
|
p.Do(m_keys);
|
||||||
p.Do(m_link_enabled);
|
p.Do(m_link_enabled);
|
||||||
p.Do(m_response_ready);
|
p.Do(m_response_size);
|
||||||
p.Do(m_response);
|
p.Do(m_joybus_buffer);
|
||||||
|
|
||||||
std::vector<u8> core_state;
|
std::vector<u8> core_state;
|
||||||
core_state.resize(m_core->stateSize(m_core));
|
core_state.resize(m_core->stateSize(m_core));
|
||||||
|
|||||||
@ -6,13 +6,9 @@
|
|||||||
#ifdef HAS_LIBMGBA
|
#ifdef HAS_LIBMGBA
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <condition_variable>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <queue>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define PYCPARSE // Remove static functions from the header
|
#define PYCPARSE // Remove static functions from the header
|
||||||
@ -23,6 +19,7 @@
|
|||||||
|
|
||||||
#include "Common/Buffer.h"
|
#include "Common/Buffer.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/WorkQueueThread.h"
|
||||||
|
|
||||||
class GBAHostInterface;
|
class GBAHostInterface;
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
@ -75,8 +72,9 @@ public:
|
|||||||
void SetForceDisconnect(bool force_disconnect);
|
void SetForceDisconnect(bool force_disconnect);
|
||||||
void EReaderQueueCard(std::string_view card_path);
|
void EReaderQueueCard(std::string_view card_path);
|
||||||
|
|
||||||
|
void SyncJoybus(u64 gc_ticks, u16 keys);
|
||||||
void SendJoybusCommand(u64 gc_ticks, int transfer_time, u8* buffer, u16 keys);
|
void SendJoybusCommand(u64 gc_ticks, int transfer_time, u8* buffer, u16 keys);
|
||||||
std::vector<u8> GetJoybusResponse();
|
int GetJoybusResponse(u8* data_out);
|
||||||
|
|
||||||
void ImportState(std::string_view state_path);
|
void ImportState(std::string_view state_path);
|
||||||
void ExportState(std::string_view state_path);
|
void ExportState(std::string_view state_path);
|
||||||
@ -88,20 +86,23 @@ public:
|
|||||||
static std::string GetSavePath(std::string_view rom_path, int device_number);
|
static std::string GetSavePath(std::string_view rom_path, int device_number);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ThreadLoop();
|
|
||||||
void RunUntil(u64 gc_ticks);
|
void RunUntil(u64 gc_ticks);
|
||||||
void RunFor(u64 gc_ticks);
|
void RunFor(u64 gc_ticks);
|
||||||
void Flush();
|
void Flush();
|
||||||
|
|
||||||
struct Command
|
enum class JoybusEventType : u8
|
||||||
{
|
{
|
||||||
u64 ticks;
|
TimeSync,
|
||||||
int transfer_time;
|
RunCommand,
|
||||||
bool sync_only;
|
|
||||||
std::array<u8, 6> buffer;
|
|
||||||
u16 keys;
|
|
||||||
};
|
};
|
||||||
void RunCommand(Command& command);
|
struct JoybusEvent
|
||||||
|
{
|
||||||
|
u64 run_until_ticks{};
|
||||||
|
u16 keys{};
|
||||||
|
JoybusEventType event_type{};
|
||||||
|
};
|
||||||
|
void PushEvent(JoybusEvent event);
|
||||||
|
void HandleEvent(JoybusEvent event);
|
||||||
|
|
||||||
bool LoadBIOS(const char* bios_path);
|
bool LoadBIOS(const char* bios_path);
|
||||||
bool LoadSave(const char* save_path);
|
bool LoadSave(const char* save_path);
|
||||||
@ -136,17 +137,19 @@ private:
|
|||||||
|
|
||||||
std::weak_ptr<GBAHostInterface> m_host;
|
std::weak_ptr<GBAHostInterface> m_host;
|
||||||
|
|
||||||
std::unique_ptr<std::thread> m_thread;
|
// Set by the GC thread before issuing a JoybusEventType::RunCommand.
|
||||||
bool m_exit_loop = false;
|
int m_joybus_command_transfer_time{};
|
||||||
bool m_idle = false;
|
GBASIOJOYCommand m_joybus_command{};
|
||||||
std::mutex m_queue_mutex;
|
|
||||||
std::condition_variable m_command_cv;
|
|
||||||
std::queue<Command> m_command_queue;
|
|
||||||
|
|
||||||
std::mutex m_response_mutex;
|
// Commands are synchronous. This buffer is used for the command and the response.
|
||||||
std::condition_variable m_response_cv;
|
std::array<u8, 5> m_joybus_buffer{}; // State saved.
|
||||||
bool m_response_ready = false;
|
|
||||||
std::vector<u8> m_response;
|
// Set by the GBA thread after filling in the above buffer.
|
||||||
|
u8 m_response_size{}; // State saved.
|
||||||
|
std::atomic_bool m_command_pending{};
|
||||||
|
|
||||||
|
// The entire threaded GBA runs within events pushed to this queue.
|
||||||
|
Common::WorkQueueThreadSP<JoybusEvent> m_event_thread;
|
||||||
|
|
||||||
::Core::System& m_system;
|
::Core::System& m_system;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -91,11 +91,9 @@ int CSIDevice_GBAEmu::RunBuffer(u8* buffer, int request_length)
|
|||||||
case NextAction::ReceiveResponse:
|
case NextAction::ReceiveResponse:
|
||||||
{
|
{
|
||||||
m_next_action = NextAction::SendCommand;
|
m_next_action = NextAction::SendCommand;
|
||||||
|
const auto response_length = m_core->GetJoybusResponse(buffer);
|
||||||
std::vector<u8> response = m_core->GetJoybusResponse();
|
if (response_length == 0)
|
||||||
if (response.empty())
|
|
||||||
return -1;
|
return -1;
|
||||||
std::ranges::copy(response, buffer);
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
const Common::Log::LogLevel log_level =
|
const Common::Log::LogLevel log_level =
|
||||||
@ -105,10 +103,10 @@ int CSIDevice_GBAEmu::RunBuffer(u8* buffer, int request_length)
|
|||||||
GENERIC_LOG_FMT(Common::Log::LogType::SERIALINTERFACE, log_level,
|
GENERIC_LOG_FMT(Common::Log::LogType::SERIALINTERFACE, log_level,
|
||||||
"{} [< {:02x}{:02x}{:02x}{:02x}{:02x}] ({})",
|
"{} [< {:02x}{:02x}{:02x}{:02x}{:02x}] ({})",
|
||||||
m_device_number, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],
|
m_device_number, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],
|
||||||
response.size());
|
response_length);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return static_cast<int>(response.size());
|
return response_length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +167,7 @@ void CSIDevice_GBAEmu::DoState(PointerWrap& p)
|
|||||||
|
|
||||||
void CSIDevice_GBAEmu::OnEvent(u64 userdata, s64 cycles_late)
|
void CSIDevice_GBAEmu::OnEvent(u64 userdata, s64 cycles_late)
|
||||||
{
|
{
|
||||||
m_core->SendJoybusCommand(m_system.GetCoreTiming().GetTicks() + userdata, 0, nullptr, m_keys);
|
m_core->SyncJoybus(m_system.GetCoreTiming().GetTicks() + userdata, m_keys);
|
||||||
|
|
||||||
const auto num_cycles = userdata + GetSyncInterval(m_system.GetSystemTimers());
|
const auto num_cycles = userdata + GetSyncInterval(m_system.GetSystemTimers());
|
||||||
m_system.GetSerialInterface().ScheduleEvent(m_device_number, num_cycles);
|
m_system.GetSerialInterface().ScheduleEvent(m_device_number, num_cycles);
|
||||||
|
|||||||
@ -95,7 +95,7 @@ static size_t s_state_writes_in_queue;
|
|||||||
static std::condition_variable s_state_write_queue_is_empty;
|
static std::condition_variable s_state_write_queue_is_empty;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
constexpr u32 STATE_VERSION = 175; // Last changed in PR 13751
|
constexpr u32 STATE_VERSION = 176; // Last changed in PR 14110
|
||||||
|
|
||||||
// Increase this if the StateExtendedHeader definition changes
|
// Increase this if the StateExtendedHeader definition changes
|
||||||
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217
|
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user