service/cam: Implement Vsync interrupt callbacks
According to HW tests, this vsync event is signaled for activated cameras at about the same frequency as the frame rate. The last 5 vsync timings are recorded (in microseconds) and can be retrieved with the service function. Also, corrected the default frame_rate to 15, according to HW test. This should fix the missing camera images in certain games.
This commit is contained in:
parent
cd46e62ad4
commit
eced511382
5 changed files with 81 additions and 4 deletions
|
@ -60,6 +60,7 @@ void Module::PortConfig::Clear() {
|
||||||
completion_event->Clear();
|
completion_event->Clear();
|
||||||
buffer_error_interrupt_event->Clear();
|
buffer_error_interrupt_event->Clear();
|
||||||
vsync_interrupt_event->Clear();
|
vsync_interrupt_event->Clear();
|
||||||
|
vsync_timings.clear();
|
||||||
is_receiving = false;
|
is_receiving = false;
|
||||||
is_active = false;
|
is_active = false;
|
||||||
is_pending_receiving = false;
|
is_pending_receiving = false;
|
||||||
|
@ -133,6 +134,27 @@ void Module::CompletionEventCallBack(u64 port_id, s64) {
|
||||||
port.completion_event->Signal();
|
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<int>(camera.frame_rate)]) - cycles_late,
|
||||||
|
vsync_interrupt_event_callback, port_id);
|
||||||
|
}
|
||||||
|
|
||||||
void Module::StartReceiving(int port_id) {
|
void Module::StartReceiving(int port_id) {
|
||||||
PortConfig& port = ports[port_id];
|
PortConfig& port = ports[port_id];
|
||||||
port.is_receiving = true;
|
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].is_active = true;
|
||||||
ports[port_id].camera_id = camera_id;
|
ports[port_id].camera_id = camera_id;
|
||||||
|
system.CoreTiming().ScheduleEvent(
|
||||||
|
msToCycles(LATENCY_BY_FRAME_RATE[static_cast<int>(cameras[camera_id].frame_rate)]),
|
||||||
|
vsync_interrupt_event_callback, port_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int max_index>
|
template <int max_index>
|
||||||
|
@ -631,6 +656,7 @@ void Module::Interface::Activate(Kernel::HLERequestContext& ctx) {
|
||||||
cam->ports[i].is_busy = false;
|
cam->ports[i].is_busy = false;
|
||||||
}
|
}
|
||||||
cam->ports[i].is_active = false;
|
cam->ports[i].is_active = false;
|
||||||
|
cam->system.CoreTiming().UnscheduleEvent(cam->vsync_interrupt_event_callback, i);
|
||||||
}
|
}
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
} else if (camera_select[0] && camera_select[1]) {
|
} else if (camera_select[0] && camera_select[1]) {
|
||||||
|
@ -860,6 +886,33 @@ void Module::Interface::SynchronizeVsyncTiming(Kernel::HLERequestContext& ctx) {
|
||||||
camera_select1, camera_select2);
|
camera_select1, camera_select2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetLatestVsyncTiming(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x2A, 2, 0);
|
||||||
|
const PortSet port_select(rp.Pop<u8>());
|
||||||
|
const u32 count = rp.Pop<u32>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
std::vector<u8> out(count * sizeof(s64_le));
|
||||||
|
std::size_t offset = 0;
|
||||||
|
for (const s64_le timing : cam->ports[port_select.m_val].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) {
|
void Module::Interface::GetStereoCameraCalibrationData(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestBuilder rb = IPC::RequestParser(ctx, 0x2B, 0, 0).MakeBuilder(17, 0);
|
IPC::RequestBuilder rb = IPC::RequestParser(ctx, 0x2B, 0, 0).MakeBuilder(17, 0);
|
||||||
|
|
||||||
|
@ -1032,6 +1085,10 @@ Module::Module(Core::System& system) : system(system) {
|
||||||
completion_event_callback = system.CoreTiming().RegisterEvent(
|
completion_event_callback = system.CoreTiming().RegisterEvent(
|
||||||
"CAM::CompletionEventCallBack",
|
"CAM::CompletionEventCallBack",
|
||||||
[this](u64 userdata, s64 cycles_late) { CompletionEventCallBack(userdata, cycles_late); });
|
[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() {
|
Module::~Module() {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <deque>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -616,6 +617,21 @@ public:
|
||||||
*/
|
*/
|
||||||
void SynchronizeVsyncTiming(Kernel::HLERequestContext& ctx);
|
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
|
* Returns calibration data relating the outside cameras to each other, for use in AR
|
||||||
* applications.
|
* applications.
|
||||||
|
@ -716,6 +732,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CompletionEventCallBack(u64 port_id, s64);
|
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
|
// Starts a receiving process on the specified port. This can only be called when is_busy = true
|
||||||
// and is_receiving = false.
|
// and is_receiving = false.
|
||||||
|
@ -744,7 +761,7 @@ private:
|
||||||
std::unique_ptr<Camera::CameraInterface> impl;
|
std::unique_ptr<Camera::CameraInterface> impl;
|
||||||
std::array<ContextConfig, 2> contexts;
|
std::array<ContextConfig, 2> contexts;
|
||||||
int current_context{0};
|
int current_context{0};
|
||||||
FrameRate frame_rate{FrameRate::Rate_5};
|
FrameRate frame_rate{FrameRate::Rate_15};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PortConfig {
|
struct PortConfig {
|
||||||
|
@ -773,6 +790,8 @@ private:
|
||||||
std::shared_ptr<Kernel::Event> buffer_error_interrupt_event;
|
std::shared_ptr<Kernel::Event> buffer_error_interrupt_event;
|
||||||
std::shared_ptr<Kernel::Event> vsync_interrupt_event;
|
std::shared_ptr<Kernel::Event> vsync_interrupt_event;
|
||||||
|
|
||||||
|
std::deque<s64> vsync_timings;
|
||||||
|
|
||||||
std::future<std::vector<u16>> capture_result; // will hold the received frame.
|
std::future<std::vector<u16>> capture_result; // will hold the received frame.
|
||||||
Kernel::Process* dest_process{nullptr};
|
Kernel::Process* dest_process{nullptr};
|
||||||
VAddr dest{0}; // the destination address of the receiving process
|
VAddr dest{0}; // the destination address of the receiving process
|
||||||
|
@ -787,6 +806,7 @@ private:
|
||||||
std::array<CameraConfig, NumCameras> cameras;
|
std::array<CameraConfig, NumCameras> cameras;
|
||||||
std::array<PortConfig, 2> ports;
|
std::array<PortConfig, 2> ports;
|
||||||
Core::TimingEventType* completion_event_callback;
|
Core::TimingEventType* completion_event_callback;
|
||||||
|
Core::TimingEventType* vsync_interrupt_event_callback;
|
||||||
std::atomic<bool> is_camera_reload_pending{false};
|
std::atomic<bool> is_camera_reload_pending{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ CAM_C::CAM_C(std::shared_ptr<Module> cam) : Module::Interface(std::move(cam), "c
|
||||||
{0x00270140, nullptr, "SetAutoWhiteBalanceWindow"},
|
{0x00270140, nullptr, "SetAutoWhiteBalanceWindow"},
|
||||||
{0x00280080, nullptr, "SetNoiseFilter"},
|
{0x00280080, nullptr, "SetNoiseFilter"},
|
||||||
{0x00290080, &CAM_C::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
|
{0x00290080, &CAM_C::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
|
||||||
{0x002A0080, nullptr, "GetLatestVsyncTiming"},
|
{0x002A0080, &CAM_C::GetLatestVsyncTiming, "GetLatestVsyncTiming"},
|
||||||
{0x002B0000, &CAM_C::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
|
{0x002B0000, &CAM_C::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
|
||||||
{0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
|
{0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
|
||||||
{0x002D00C0, nullptr, "WriteRegisterI2c"},
|
{0x002D00C0, nullptr, "WriteRegisterI2c"},
|
||||||
|
|
|
@ -50,7 +50,7 @@ CAM_S::CAM_S(std::shared_ptr<Module> cam) : Module::Interface(std::move(cam), "c
|
||||||
{0x00270140, nullptr, "SetAutoWhiteBalanceWindow"},
|
{0x00270140, nullptr, "SetAutoWhiteBalanceWindow"},
|
||||||
{0x00280080, nullptr, "SetNoiseFilter"},
|
{0x00280080, nullptr, "SetNoiseFilter"},
|
||||||
{0x00290080, &CAM_S::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
|
{0x00290080, &CAM_S::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
|
||||||
{0x002A0080, nullptr, "GetLatestVsyncTiming"},
|
{0x002A0080, &CAM_S::GetLatestVsyncTiming, "GetLatestVsyncTiming"},
|
||||||
{0x002B0000, &CAM_S::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
|
{0x002B0000, &CAM_S::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
|
||||||
{0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
|
{0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
|
||||||
{0x002D00C0, nullptr, "WriteRegisterI2c"},
|
{0x002D00C0, nullptr, "WriteRegisterI2c"},
|
||||||
|
|
|
@ -50,7 +50,7 @@ CAM_U::CAM_U(std::shared_ptr<Module> cam) : Module::Interface(std::move(cam), "c
|
||||||
{0x00270140, nullptr, "SetAutoWhiteBalanceWindow"},
|
{0x00270140, nullptr, "SetAutoWhiteBalanceWindow"},
|
||||||
{0x00280080, nullptr, "SetNoiseFilter"},
|
{0x00280080, nullptr, "SetNoiseFilter"},
|
||||||
{0x00290080, &CAM_U::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
|
{0x00290080, &CAM_U::SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
|
||||||
{0x002A0080, nullptr, "GetLatestVsyncTiming"},
|
{0x002A0080, &CAM_U::GetLatestVsyncTiming, "GetLatestVsyncTiming"},
|
||||||
{0x002B0000, &CAM_U::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
|
{0x002B0000, &CAM_U::GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
|
||||||
{0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
|
{0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
|
||||||
{0x002D00C0, nullptr, "WriteRegisterI2c"},
|
{0x002D00C0, nullptr, "WriteRegisterI2c"},
|
||||||
|
|
Loading…
Reference in a new issue