mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-12 06:39:26 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a14f37ed8 | |||
| 5fcfa46ca8 |
@@ -298,7 +298,7 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitCommandList(
|
||||
static std::vector<Tegra::CommandHeader> BuildWaitCommandList(
|
||||
NvFence fence) {
|
||||
return {
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
|
||||
@@ -310,9 +310,9 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitComman
|
||||
};
|
||||
}
|
||||
|
||||
static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementCommandList(
|
||||
static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(
|
||||
NvFence fence) {
|
||||
boost::container::small_vector<Tegra::CommandHeader, 512> result{
|
||||
std::vector<Tegra::CommandHeader> result{
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
|
||||
Tegra::SubmissionMode::Increasing),
|
||||
{}};
|
||||
@@ -327,9 +327,9 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementC
|
||||
return result;
|
||||
}
|
||||
|
||||
static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementWithWfiCommandList(
|
||||
static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(
|
||||
NvFence fence) {
|
||||
boost::container::small_vector<Tegra::CommandHeader, 512> result{
|
||||
std::vector<Tegra::CommandHeader> result{
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1,
|
||||
Tegra::SubmissionMode::Increasing),
|
||||
{}};
|
||||
|
||||
@@ -28,10 +28,8 @@ JoyconDriver::~JoyconDriver() {
|
||||
}
|
||||
|
||||
void JoyconDriver::Stop() {
|
||||
if (input_thread.joinable()) {
|
||||
input_thread.request_stop();
|
||||
input_thread.join();
|
||||
}
|
||||
is_connected = false;
|
||||
input_thread = {};
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
|
||||
@@ -60,6 +58,7 @@ Common::Input::DriverResult JoyconDriver::InitializeDevice() {
|
||||
return Common::Input::DriverResult::InvalidHandle;
|
||||
}
|
||||
std::scoped_lock lock{mutex};
|
||||
disable_input_thread = true;
|
||||
|
||||
// Reset Counters
|
||||
error_counter = 0;
|
||||
@@ -127,44 +126,62 @@ Common::Input::DriverResult JoyconDriver::InitializeDevice() {
|
||||
right_stick_calibration, motion_calibration);
|
||||
|
||||
// Start polling for data
|
||||
if (!input_thread.joinable()) {
|
||||
input_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
InputThread(stop_token);
|
||||
});
|
||||
is_connected = true;
|
||||
if (!input_thread_running) {
|
||||
input_thread =
|
||||
std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
|
||||
}
|
||||
|
||||
disable_input_thread = false;
|
||||
return Common::Input::DriverResult::Success;
|
||||
}
|
||||
|
||||
void JoyconDriver::InputThread(std::stop_token stop_token) {
|
||||
LOG_INFO(Input, "Joycon Adapter input thread started");
|
||||
Common::SetCurrentThreadName("JoyconInput");
|
||||
input_thread_running = true;
|
||||
|
||||
// Max update rate is 5ms, ensure we are always able to read a bit faster
|
||||
constexpr int ThreadDelay = 3;
|
||||
std::vector<u8> buffer(MaxBufferSize);
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
// Max update rate is 5ms, so just (timeout) at 300ms
|
||||
constexpr int READ_TIMEOUT_MS = 300;
|
||||
constexpr size_t MAX_VIBRATIONS = 4;
|
||||
std::array<u8, MaxBufferSize> buffer; // Filled by SDL, don't zero-init
|
||||
int status = 0;
|
||||
if (IsInputThreadValid()) {
|
||||
// By disabling the input thread we can ensure custom commands will succeed as no package is
|
||||
// skipped
|
||||
status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(), READ_TIMEOUT_MS);
|
||||
if (IsPayloadCorrect(status, buffer)) {
|
||||
OnNewData(buffer);
|
||||
}
|
||||
if (!vibration_queue.Empty()) {
|
||||
VibrationValue vibration_value;
|
||||
vibration_queue.Pop(vibration_value);
|
||||
last_vibration_result = rumble_protocol->SendVibration(vibration_value);
|
||||
}
|
||||
// We can't keep up with vibrations. Start skipping.
|
||||
while (vibration_queue.Size() >= MAX_VIBRATIONS) {
|
||||
vibration_queue.Pop();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (!IsInputThreadValid()) {
|
||||
input_thread.request_stop();
|
||||
continue;
|
||||
}
|
||||
|
||||
// By disabling the input thread we can ensure custom commands will succeed as no package is
|
||||
// skipped
|
||||
if (!disable_input_thread) {
|
||||
status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
|
||||
ThreadDelay);
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay));
|
||||
}
|
||||
|
||||
if (IsPayloadCorrect(status, buffer)) {
|
||||
OnNewData(buffer);
|
||||
}
|
||||
|
||||
if (!vibration_queue.Empty()) {
|
||||
VibrationValue vibration_value;
|
||||
vibration_queue.Pop(vibration_value);
|
||||
last_vibration_result = rumble_protocol->SendVibration(vibration_value);
|
||||
}
|
||||
|
||||
// We can't keep up with vibrations. Start skipping.
|
||||
while (vibration_queue.Size() > 6) {
|
||||
vibration_queue.Pop();
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
is_connected = false;
|
||||
input_thread_running = false;
|
||||
LOG_INFO(Input, "Joycon Adapter input thread stopped");
|
||||
}
|
||||
|
||||
@@ -254,6 +271,11 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::SetPollingMode() {
|
||||
SCOPE_EXIT {
|
||||
disable_input_thread = false;
|
||||
};
|
||||
disable_input_thread = true;
|
||||
|
||||
rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
|
||||
|
||||
if (motion_enabled && supported_features.motion) {
|
||||
@@ -360,12 +382,17 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
|
||||
}
|
||||
|
||||
bool JoyconDriver::IsInputThreadValid() const {
|
||||
if (hidapi_handle == nullptr || hidapi_handle->handle == nullptr)
|
||||
if (!is_connected.load()) {
|
||||
return false;
|
||||
}
|
||||
if (hidapi_handle->handle == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// Controller is not responding. Terminate connection
|
||||
if (error_counter > MaxErrorCount)
|
||||
if (error_counter > MaxErrorCount) {
|
||||
return false;
|
||||
return input_thread.joinable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
|
||||
@@ -388,18 +415,30 @@ bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (disable_input_thread) {
|
||||
return Common::Input::DriverResult::HandleInUse;
|
||||
}
|
||||
vibration_queue.Push(vibration);
|
||||
return last_vibration_result;
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (disable_input_thread) {
|
||||
return Common::Input::DriverResult::HandleInUse;
|
||||
}
|
||||
return generic_protocol->SetLedPattern(led_pattern);
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
|
||||
std::scoped_lock lock{mutex};
|
||||
return irs_protocol->SetIrsConfig(mode_, format_);
|
||||
if (disable_input_thread) {
|
||||
return Common::Input::DriverResult::HandleInUse;
|
||||
}
|
||||
disable_input_thread = true;
|
||||
const auto result = irs_protocol->SetIrsConfig(mode_, format_);
|
||||
disable_input_thread = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::SetPassiveMode() {
|
||||
@@ -493,7 +532,12 @@ Common::Input::DriverResult JoyconDriver::StartNfcPolling() {
|
||||
if (!nfc_protocol->IsEnabled()) {
|
||||
return Common::Input::DriverResult::Disabled;
|
||||
}
|
||||
return nfc_protocol->StartNFCPollingMode();
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->StartNFCPollingMode();
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
|
||||
@@ -506,7 +550,10 @@ Common::Input::DriverResult JoyconDriver::StopNfcPolling() {
|
||||
return Common::Input::DriverResult::Disabled;
|
||||
}
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->StopNFCPollingMode();
|
||||
disable_input_thread = false;
|
||||
|
||||
if (amiibo_detected) {
|
||||
amiibo_detected = false;
|
||||
joycon_poller->UpdateAmiibo({});
|
||||
@@ -529,7 +576,11 @@ Common::Input::DriverResult JoyconDriver::ReadAmiiboData(std::vector<u8>& out_da
|
||||
}
|
||||
|
||||
out_data.resize(0x21C);
|
||||
return nfc_protocol->ReadAmiibo(out_data);
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->ReadAmiibo(out_data);
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
|
||||
@@ -544,7 +595,12 @@ Common::Input::DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data)
|
||||
if (!amiibo_detected) {
|
||||
return Common::Input::DriverResult::ErrorWritingData;
|
||||
}
|
||||
return nfc_protocol->WriteAmiibo(data);
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->WriteAmiibo(data);
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareReadChunk> data,
|
||||
@@ -560,7 +616,12 @@ Common::Input::DriverResult JoyconDriver::ReadMifareData(std::span<const MifareR
|
||||
if (!amiibo_detected) {
|
||||
return Common::Input::DriverResult::ErrorWritingData;
|
||||
}
|
||||
return nfc_protocol->ReadMifare(data, out_data);
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->ReadMifare(data, out_data);
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const MifareWriteChunk> data) {
|
||||
@@ -575,11 +636,17 @@ Common::Input::DriverResult JoyconDriver::WriteMifareData(std::span<const Mifare
|
||||
if (!amiibo_detected) {
|
||||
return Common::Input::DriverResult::ErrorWritingData;
|
||||
}
|
||||
return nfc_protocol->WriteMifare(data);
|
||||
|
||||
disable_input_thread = true;
|
||||
const auto result = nfc_protocol->WriteMifare(data);
|
||||
disable_input_thread = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool JoyconDriver::IsConnected() const {
|
||||
return input_thread.joinable();
|
||||
std::scoped_lock lock{mutex};
|
||||
return is_connected.load();
|
||||
}
|
||||
|
||||
bool JoyconDriver::IsVibrationEnabled() const {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -163,6 +160,8 @@ private:
|
||||
// Thread related
|
||||
mutable std::mutex mutex;
|
||||
std::jthread input_thread;
|
||||
bool input_thread_running{};
|
||||
bool disable_input_thread{};
|
||||
};
|
||||
|
||||
} // namespace InputCommon::Joycon
|
||||
|
||||
@@ -22,7 +22,7 @@ void JoyconPoller::SetCallbacks(const JoyconCallbacks& callbacks_) {
|
||||
void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
|
||||
const RingStatus& ring_status) {
|
||||
InputReportActive data{};
|
||||
std::memcpy(&data, buffer.data(), sizeof(InputReportActive));
|
||||
memcpy(&data, buffer.data(), sizeof(InputReportActive));
|
||||
|
||||
switch (device_type) {
|
||||
case ControllerType::Left:
|
||||
@@ -47,7 +47,7 @@ void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& moti
|
||||
|
||||
void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
|
||||
InputReportPassive data{};
|
||||
std::memcpy(&data, buffer.data(), sizeof(InputReportPassive));
|
||||
memcpy(&data, buffer.data(), sizeof(InputReportPassive));
|
||||
|
||||
switch (device_type) {
|
||||
case ControllerType::Left:
|
||||
|
||||
@@ -109,11 +109,11 @@ inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, Sub
|
||||
struct CommandList final {
|
||||
CommandList() = default;
|
||||
explicit CommandList(std::size_t size) : command_lists(size) {}
|
||||
explicit CommandList(boost::container::small_vector<CommandHeader, 512>&& prefetch_command_list_)
|
||||
: prefetch_command_list{std::move(prefetch_command_list_)} {}
|
||||
|
||||
boost::container::small_vector<CommandListHeader, 512> command_lists;
|
||||
boost::container::small_vector<CommandHeader, 512> prefetch_command_list;
|
||||
explicit CommandList(std::vector<CommandHeader>&& prefetch_command_list_)
|
||||
: prefetch_command_list{std::move(prefetch_command_list_)}
|
||||
{}
|
||||
std::vector<CommandListHeader> command_lists;
|
||||
std::vector<CommandHeader> prefetch_command_list;
|
||||
};
|
||||
|
||||
/// @brief The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the
|
||||
|
||||
Reference in New Issue
Block a user