IR: Move to scheduler

This commit is contained in:
wheremyfoodat 2025-07-03 02:42:43 +03:00
parent b2904f391f
commit dc7f8a48bd
7 changed files with 29 additions and 14 deletions

View file

@ -12,7 +12,8 @@ struct Scheduler {
UpdateTimers = 1, // Update kernel timer objects UpdateTimers = 1, // Update kernel timer objects
RunDSP = 2, // Make the emulated DSP run for one audio frame RunDSP = 2, // Make the emulated DSP run for one audio frame
SignalY2R = 3, // Signal that a Y2R conversion has finished SignalY2R = 3, // Signal that a Y2R conversion has finished
Panic = 4, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX) UpdateIR = 4, // Update an IR device (For now, just the CirclePad Pro/N3DS controls)
Panic = 5, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX)
TotalNumberOfEvents // How many event types do we have in total? TotalNumberOfEvents // How many event types do we have in total?
}; };
static constexpr usize totalNumberOfEvents = static_cast<usize>(EventType::TotalNumberOfEvents); static constexpr usize totalNumberOfEvents = static_cast<usize>(EventType::TotalNumberOfEvents);

View file

@ -44,7 +44,10 @@ namespace IR {
virtual void disconnect() override; virtual void disconnect() override;
virtual void receivePayload(Payload payload) override; virtual void receivePayload(Payload payload) override;
CirclePadPro(SendCallback sendCallback) : IR::Device(sendCallback) {} CirclePadPro(SendCallback sendCallback, Scheduler& scheduler) : IR::Device(sendCallback, scheduler) {}
ButtonState state; ButtonState state;
// The current polling period in cycles, configured via the ConfigurePolling command
s64 period = Scheduler::nsToCycles(16000);
}; };
} // namespace IR } // namespace IR

View file

@ -3,6 +3,7 @@
#include <span> #include <span>
#include "helpers.hpp" #include "helpers.hpp"
#include "scheduler.hpp"
namespace IR { namespace IR {
class Device { class Device {
@ -11,6 +12,7 @@ namespace IR {
protected: protected:
using SendCallback = std::function<void(Payload)>; // Callback for sending data from IR device->3DS using SendCallback = std::function<void(Payload)>; // Callback for sending data from IR device->3DS
Scheduler& scheduler;
SendCallback sendCallback; SendCallback sendCallback;
public: public:
@ -18,6 +20,6 @@ namespace IR {
virtual void disconnect() = 0; virtual void disconnect() = 0;
virtual void receivePayload(Payload payload) = 0; virtual void receivePayload(Payload payload) = 0;
Device(SendCallback sendCallback) : sendCallback(sendCallback) {} Device(SendCallback sendCallback, Scheduler& scheduler) : sendCallback(sendCallback), scheduler(scheduler) {}
}; };
} // namespace IR } // namespace IR

View file

@ -16,6 +16,7 @@
class Kernel; class Kernel;
class IRUserService { class IRUserService {
using Payload = IR::Device::Payload;
using Handle = HorizonHandle; using Handle = HorizonHandle;
enum class DeviceID : u8 { enum class DeviceID : u8 {
@ -75,14 +76,11 @@ class IRUserService {
// The IR service uses CRC8 with generator polynomial = 0x07 for verifying packets received from IR devices // The IR service uses CRC8 with generator polynomial = 0x07 for verifying packets received from IR devices
static u8 crc8(std::span<const u8> data); static u8 crc8(std::span<const u8> data);
// IR service calls this to send a console->device payload
void receivePayload(std::span<const u8> data);
// IR devices call this to send a device->console payload // IR devices call this to send a device->console payload
void sendPayload(std::span<const u8> payload); void sendPayload(Payload payload);
public: public:
IRUserService(Memory& mem, HIDService& hid, const EmulatorConfig& config, Kernel& kernel) IRUserService(Memory& mem, HIDService& hid, const EmulatorConfig& config, Kernel& kernel);
: mem(mem), hid(hid), config(config), kernel(kernel), cpp([&](IR::Device::Payload payload) { sendPayload(payload); }) {}
void setCStickX(s16 value) { cpp.state.cStick.x = value; } void setCStickX(s16 value) { cpp.state.cStick.x = value; }
void setCStickY(s16 value) { cpp.state.cStick.y = value; } void setCStickY(s16 value) { cpp.state.cStick.y = value; }

View file

@ -7,15 +7,20 @@
using namespace IR; using namespace IR;
void CirclePadPro::connect() {} void CirclePadPro::connect() {}
void CirclePadPro::disconnect() {} void CirclePadPro::disconnect() { scheduler.removeEvent(Scheduler::EventType::UpdateIR); }
void CirclePadPro::receivePayload(Payload payload) { void CirclePadPro::receivePayload(Payload payload) {
const u8 type = payload[0]; const u8 type = payload[0];
switch (type) { switch (type) {
case CPPRequestID::ConfigurePolling: { case CPPRequestID::ConfigurePolling: {
[[maybe_unused]] const u8 pollingPeriodMs = payload[1]; // Convert polling period from ms to ns for easier use with the scheduler
// TODO const s64 periodNs = s64(payload[1]) * 1000ll;
// Convert to cycles
period = Scheduler::nsToCycles(periodNs);
scheduler.removeEvent(Scheduler::EventType::UpdateIR);
scheduler.addEvent(Scheduler::EventType::UpdateIR, scheduler.currentTimestamp + period);
break; break;
} }

View file

@ -28,6 +28,9 @@ namespace IRUserCommands {
}; };
} }
IRUserService::IRUserService(Memory& mem, HIDService& hid, const EmulatorConfig& config, Kernel& kernel)
: mem(mem), hid(hid), config(config), kernel(kernel), cpp([&](IR::Device::Payload payload) { sendPayload(payload); }, kernel.getScheduler()) {}
void IRUserService::reset() { void IRUserService::reset() {
connectionStatusEvent = std::nullopt; connectionStatusEvent = std::nullopt;
receiveEvent = std::nullopt; receiveEvent = std::nullopt;
@ -285,7 +288,7 @@ void IRUserService::clearSendBuffer(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 4, Result::Success);
} }
void IRUserService::sendPayload(std::span<const u8> payload) { void IRUserService::sendPayload(IRUserService::Payload payload) {
if (!receiveBuffer) { if (!receiveBuffer) {
return; return;
} }
@ -343,4 +346,8 @@ void IRUserService::updateCirclePadPro() {
std::vector<u8> response(sizeof(cppState)); std::vector<u8> response(sizeof(cppState));
std::memcpy(response.data(), &cppState, sizeof(cppState)); std::memcpy(response.data(), &cppState, sizeof(cppState));
sendPayload(response); sendPayload(response);
// Schedule next IR event. TODO: Maybe account for cycle drift.
auto& scheduler = kernel.getScheduler();
scheduler.addEvent(Scheduler::EventType::UpdateIR, scheduler.currentTimestamp + cpp.period);
} }

View file

@ -139,8 +139,6 @@ void Emulator::runFrame() {
if (cheats.haveCheats()) [[unlikely]] { if (cheats.haveCheats()) [[unlikely]] {
cheats.run(); cheats.run();
} }
getServiceManager().getIRUser().updateCirclePadPro();
} else if (romType != ROMType::None) { } else if (romType != ROMType::None) {
// If the emulator is not running and a game is loaded, we still want to display the framebuffer otherwise we will get weird // If the emulator is not running and a game is loaded, we still want to display the framebuffer otherwise we will get weird
// double-buffering issues // double-buffering issues
@ -182,6 +180,7 @@ void Emulator::pollScheduler() {
} }
case Scheduler::EventType::SignalY2R: kernel.getServiceManager().getY2R().signalConversionDone(); break; case Scheduler::EventType::SignalY2R: kernel.getServiceManager().getY2R().signalConversionDone(); break;
case Scheduler::EventType::UpdateIR: kernel.getServiceManager().getIRUser().updateCirclePadPro(); break;
default: { default: {
Helpers::panic("Scheduler: Unimplemented event type received: %d\n", static_cast<int>(eventType)); Helpers::panic("Scheduler: Unimplemented event type received: %d\n", static_cast<int>(eventType));