diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index bf1bab8c0c..c5d24c3144 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -60,6 +60,7 @@ void Module::PortConfig::Clear() { completion_event->Clear(); buffer_error_interrupt_event->Clear(); vsync_interrupt_event->Clear(); + vsync_timings.clear(); is_receiving = false; is_active = false; is_pending_receiving = false; @@ -133,6 +134,27 @@ void Module::CompletionEventCallBack(u64 port_id, s64) { port.completion_event->Signal(); } +static constexpr std::size_t MaxVsyncTimings = 5; + +void Module::VsyncInterruptEventCallBack(u64 port_id, s64 cycles_late) { + PortConfig& port = ports[port_id]; + const CameraConfig& camera = cameras[port.camera_id]; + + if (!port.is_active) { + return; + } + + port.vsync_timings.emplace_front(system.CoreTiming().GetGlobalTimeUs().count()); + if (port.vsync_timings.size() > MaxVsyncTimings) { + port.vsync_timings.pop_back(); + } + port.vsync_interrupt_event->Signal(); + + system.CoreTiming().ScheduleEvent( + msToCycles(LATENCY_BY_FRAME_RATE[static_cast(camera.frame_rate)]) - cycles_late, + vsync_interrupt_event_callback, port_id); +} + void Module::StartReceiving(int port_id) { PortConfig& port = ports[port_id]; port.is_receiving = true; @@ -173,6 +195,9 @@ void Module::ActivatePort(int port_id, int camera_id) { } ports[port_id].is_active = true; ports[port_id].camera_id = camera_id; + system.CoreTiming().ScheduleEvent( + msToCycles(LATENCY_BY_FRAME_RATE[static_cast(cameras[camera_id].frame_rate)]), + vsync_interrupt_event_callback, port_id); } template @@ -631,6 +656,7 @@ void Module::Interface::Activate(Kernel::HLERequestContext& ctx) { cam->ports[i].is_busy = false; } cam->ports[i].is_active = false; + cam->system.CoreTiming().UnscheduleEvent(cam->vsync_interrupt_event_callback, i); } rb.Push(RESULT_SUCCESS); } else if (camera_select[0] && camera_select[1]) { @@ -860,6 +886,34 @@ void Module::Interface::SynchronizeVsyncTiming(Kernel::HLERequestContext& ctx) { camera_select1, camera_select2); } +void Module::Interface::GetLatestVsyncTiming(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2A, 2, 0); + const PortSet port_select(rp.Pop()); + const u32 count = rp.Pop(); + + if (!port_select.IsSingle() || count > MaxVsyncTimings) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(ERROR_OUT_OF_RANGE); + rb.PushStaticBuffer({}, 0); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + + const std::size_t port_id = port_select.m_val == 1 ? 0 : 1; + std::vector out(count * sizeof(s64_le)); + std::size_t offset = 0; + for (const s64_le timing : cam->ports[port_id].vsync_timings) { + std::memcpy(out.data() + offset * sizeof(timing), &timing, sizeof(timing)); + offset++; + if (offset >= count) { + break; + } + } + rb.PushStaticBuffer(out, 0); +} + void Module::Interface::GetStereoCameraCalibrationData(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = IPC::RequestParser(ctx, 0x2B, 0, 0).MakeBuilder(17, 0); @@ -1032,6 +1086,10 @@ Module::Module(Core::System& system) : system(system) { completion_event_callback = system.CoreTiming().RegisterEvent( "CAM::CompletionEventCallBack", [this](u64 userdata, s64 cycles_late) { CompletionEventCallBack(userdata, cycles_late); }); + vsync_interrupt_event_callback = system.CoreTiming().RegisterEvent( + "CAM::VsyncInterruptEventCallBack", [this](u64 userdata, s64 cycles_late) { + VsyncInterruptEventCallBack(userdata, cycles_late); + }); } Module::~Module() { diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index 9f3d819901..989a3a6a74 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -616,6 +617,21 @@ public: */ void SynchronizeVsyncTiming(Kernel::HLERequestContext& ctx); + /** + * Gets the vsync timing record of the specified camera for the specified number of signals. + * Inputs: + * 0: 0x002A0080 + * 1: Port + * 2: Number of timings to get + * 64: ((PastTimings * 8) << 14) | 2 + * 65: s64* TimingsOutput + * Outputs: + * 0: 0x002A0042 + * 1: ResultCode + * 2-3: Output static buffer + */ + void GetLatestVsyncTiming(Kernel::HLERequestContext& ctx); + /** * Returns calibration data relating the outside cameras to each other, for use in AR * applications. @@ -716,6 +732,7 @@ public: private: void CompletionEventCallBack(u64 port_id, s64); + void VsyncInterruptEventCallBack(u64 port_id, s64 cycles_late); // Starts a receiving process on the specified port. This can only be called when is_busy = true // and is_receiving = false. @@ -744,7 +761,7 @@ private: std::unique_ptr impl; std::array contexts; int current_context{0}; - FrameRate frame_rate{FrameRate::Rate_5}; + FrameRate frame_rate{FrameRate::Rate_15}; }; struct PortConfig { @@ -773,6 +790,8 @@ private: std::shared_ptr buffer_error_interrupt_event; std::shared_ptr vsync_interrupt_event; + std::deque vsync_timings; + std::future> capture_result; // will hold the received frame. Kernel::Process* dest_process{nullptr}; VAddr dest{0}; // the destination address of the receiving process @@ -787,6 +806,7 @@ private: std::array cameras; std::array ports; Core::TimingEventType* completion_event_callback; + Core::TimingEventType* vsync_interrupt_event_callback; std::atomic is_camera_reload_pending{false}; }; diff --git a/src/core/hle/service/cam/cam_c.cpp b/src/core/hle/service/cam/cam_c.cpp index 80816c9237..e5aca5361a 100644 --- a/src/core/hle/service/cam/cam_c.cpp +++ b/src/core/hle/service/cam/cam_c.cpp @@ -50,7 +50,7 @@ CAM_C::CAM_C(std::shared_ptr cam) : Module::Interface(std::move(cam), "c {0x00270140, nullptr, "SetAutoWhiteBalanceWindow"}, {0x00280080, nullptr, "SetNoiseFilter"}, {0x00290080, &CAM_C::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"}, - {0x002A0080, nullptr, "GetLatestVsyncTiming"}, + {0x002A0080, &CAM_C::GetLatestVsyncTiming, "GetLatestVsyncTiming"}, {0x002B0000, &CAM_C::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"}, {0x002C0400, nullptr, "SetStereoCameraCalibrationData"}, {0x002D00C0, nullptr, "WriteRegisterI2c"}, diff --git a/src/core/hle/service/cam/cam_s.cpp b/src/core/hle/service/cam/cam_s.cpp index c4936f9b54..606c8d3af7 100644 --- a/src/core/hle/service/cam/cam_s.cpp +++ b/src/core/hle/service/cam/cam_s.cpp @@ -50,7 +50,7 @@ CAM_S::CAM_S(std::shared_ptr cam) : Module::Interface(std::move(cam), "c {0x00270140, nullptr, "SetAutoWhiteBalanceWindow"}, {0x00280080, nullptr, "SetNoiseFilter"}, {0x00290080, &CAM_S::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"}, - {0x002A0080, nullptr, "GetLatestVsyncTiming"}, + {0x002A0080, &CAM_S::GetLatestVsyncTiming, "GetLatestVsyncTiming"}, {0x002B0000, &CAM_S::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"}, {0x002C0400, nullptr, "SetStereoCameraCalibrationData"}, {0x002D00C0, nullptr, "WriteRegisterI2c"}, diff --git a/src/core/hle/service/cam/cam_u.cpp b/src/core/hle/service/cam/cam_u.cpp index 3c3c4b17df..83cace5a70 100644 --- a/src/core/hle/service/cam/cam_u.cpp +++ b/src/core/hle/service/cam/cam_u.cpp @@ -50,7 +50,7 @@ CAM_U::CAM_U(std::shared_ptr cam) : Module::Interface(std::move(cam), "c {0x00270140, nullptr, "SetAutoWhiteBalanceWindow"}, {0x00280080, nullptr, "SetNoiseFilter"}, {0x00290080, &CAM_U::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"}, - {0x002A0080, nullptr, "GetLatestVsyncTiming"}, + {0x002A0080, &CAM_U::GetLatestVsyncTiming, "GetLatestVsyncTiming"}, {0x002B0000, &CAM_U::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"}, {0x002C0400, nullptr, "SetStereoCameraCalibrationData"}, {0x002D00C0, nullptr, "WriteRegisterI2c"},