summaryrefslogtreecommitdiff
path: root/src/hid_core/irsensor
diff options
context:
space:
mode:
authorGravatar Narr the Reg2024-01-04 20:37:43 -0600
committerGravatar Narr the Reg2024-01-05 11:41:15 -0600
commitee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch)
tree3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/hid_core/irsensor
parentMerge pull request #12437 from ameerj/gl-amd-fixes (diff)
downloadyuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.gz
yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.tar.xz
yuzu-ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613.zip
hid_core: Move hid to it's own subproject
Diffstat (limited to 'src/hid_core/irsensor')
-rw-r--r--src/hid_core/irsensor/clustering_processor.cpp267
-rw-r--r--src/hid_core/irsensor/clustering_processor.h115
-rw-r--r--src/hid_core/irsensor/image_transfer_processor.cpp155
-rw-r--r--src/hid_core/irsensor/image_transfer_processor.h77
-rw-r--r--src/hid_core/irsensor/ir_led_processor.cpp27
-rw-r--r--src/hid_core/irsensor/ir_led_processor.h47
-rw-r--r--src/hid_core/irsensor/irs_types.h301
-rw-r--r--src/hid_core/irsensor/moment_processor.cpp149
-rw-r--r--src/hid_core/irsensor/moment_processor.h91
-rw-r--r--src/hid_core/irsensor/pointing_processor.cpp26
-rw-r--r--src/hid_core/irsensor/pointing_processor.h61
-rw-r--r--src/hid_core/irsensor/processor_base.cpp67
-rw-r--r--src/hid_core/irsensor/processor_base.h33
-rw-r--r--src/hid_core/irsensor/tera_plugin_processor.cpp29
-rw-r--r--src/hid_core/irsensor/tera_plugin_processor.h53
15 files changed, 1498 insertions, 0 deletions
diff --git a/src/hid_core/irsensor/clustering_processor.cpp b/src/hid_core/irsensor/clustering_processor.cpp
new file mode 100644
index 000000000..3abe19365
--- /dev/null
+++ b/src/hid_core/irsensor/clustering_processor.cpp
@@ -0,0 +1,267 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <queue>
5
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "hid_core/frontend/emulated_controller.h"
9#include "hid_core/hid_core.h"
10#include "hid_core/irsensor/clustering_processor.h"
11
12namespace Service::IRS {
13ClusteringProcessor::ClusteringProcessor(Core::System& system_,
14 Core::IrSensor::DeviceFormat& device_format,
15 std::size_t npad_index)
16 : device{device_format}, system{system_} {
17 npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
18
19 device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
20 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
21 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
22 SetDefaultConfig();
23
24 shared_memory = std::construct_at(
25 reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
26
27 Core::HID::ControllerUpdateCallback engine_callback{
28 .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
29 .is_npad_service = true,
30 };
31 callback_key = npad_device->SetCallback(engine_callback);
32}
33
34ClusteringProcessor::~ClusteringProcessor() {
35 npad_device->DeleteCallback(callback_key);
36};
37
38void ClusteringProcessor::StartProcessor() {
39 device.camera_status = Core::IrSensor::IrCameraStatus::Available;
40 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
41}
42
43void ClusteringProcessor::SuspendProcessor() {}
44
45void ClusteringProcessor::StopProcessor() {}
46
47void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
48 if (type != Core::HID::ControllerTriggerType::IrSensor) {
49 return;
50 }
51
52 next_state = {};
53 const auto& camera_data = npad_device->GetCamera();
54 auto filtered_image = camera_data.data;
55
56 RemoveLowIntensityData(filtered_image);
57
58 const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
59 const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
60 const auto window_end_x =
61 window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
62 const auto window_end_y =
63 window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
64
65 for (std::size_t y = window_start_y; y < window_end_y; y++) {
66 for (std::size_t x = window_start_x; x < window_end_x; x++) {
67 u8 pixel = GetPixel(filtered_image, x, y);
68 if (pixel == 0) {
69 continue;
70 }
71 const auto cluster = GetClusterProperties(filtered_image, x, y);
72 if (cluster.pixel_count > current_config.pixel_count_max) {
73 continue;
74 }
75 if (cluster.pixel_count < current_config.pixel_count_min) {
76 continue;
77 }
78 // Cluster object limit reached
79 if (next_state.object_count >= next_state.data.size()) {
80 continue;
81 }
82 next_state.data[next_state.object_count] = cluster;
83 next_state.object_count++;
84 }
85 }
86
87 next_state.sampling_number = camera_data.sample;
88 next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
89 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
90 shared_memory->clustering_lifo.WriteNextEntry(next_state);
91
92 if (!IsProcessorActive()) {
93 StartProcessor();
94 }
95}
96
97void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
98 for (u8& pixel : data) {
99 if (pixel < current_config.pixel_count_min) {
100 pixel = 0;
101 }
102 }
103}
104
105ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
106 std::size_t x,
107 std::size_t y) {
108 using DataPoint = Common::Point<std::size_t>;
109 std::queue<DataPoint> search_points{};
110 ClusteringData current_cluster = GetPixelProperties(data, x, y);
111 SetPixel(data, x, y, 0);
112 search_points.emplace<DataPoint>({x, y});
113
114 while (!search_points.empty()) {
115 const auto point = search_points.front();
116 search_points.pop();
117
118 // Avoid negative numbers
119 if (point.x == 0 || point.y == 0) {
120 continue;
121 }
122
123 std::array<DataPoint, 4> new_points{
124 DataPoint{point.x - 1, point.y},
125 {point.x, point.y - 1},
126 {point.x + 1, point.y},
127 {point.x, point.y + 1},
128 };
129
130 for (const auto new_point : new_points) {
131 if (new_point.x >= width) {
132 continue;
133 }
134 if (new_point.y >= height) {
135 continue;
136 }
137 if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
138 continue;
139 }
140 const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
141 current_cluster = MergeCluster(current_cluster, cluster);
142 SetPixel(data, new_point.x, new_point.y, 0);
143 search_points.emplace<DataPoint>({new_point.x, new_point.y});
144 }
145 }
146
147 return current_cluster;
148}
149
150ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
151 const std::vector<u8>& data, std::size_t x, std::size_t y) const {
152 return {
153 .average_intensity = GetPixel(data, x, y) / 255.0f,
154 .centroid =
155 {
156 .x = static_cast<f32>(x),
157 .y = static_cast<f32>(y),
158
159 },
160 .pixel_count = 1,
161 .bound =
162 {
163 .x = static_cast<s16>(x),
164 .y = static_cast<s16>(y),
165 .width = 1,
166 .height = 1,
167 },
168 };
169}
170
171ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
172 const ClusteringData a, const ClusteringData b) const {
173 const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
174 const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
175 const f32 pixel_count = a_pixel_count + b_pixel_count;
176 const f32 average_intensity =
177 (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
178 const Core::IrSensor::IrsCentroid centroid = {
179 .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
180 .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
181 };
182 s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
183 s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
184 s16 a_bound_end_x = a.bound.x + a.bound.width;
185 s16 a_bound_end_y = a.bound.y + a.bound.height;
186 s16 b_bound_end_x = b.bound.x + b.bound.width;
187 s16 b_bound_end_y = b.bound.y + b.bound.height;
188
189 const Core::IrSensor::IrsRect bound = {
190 .x = bound_start_x,
191 .y = bound_start_y,
192 .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
193 : static_cast<s16>(b_bound_end_x - bound_start_x),
194 .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
195 : static_cast<s16>(b_bound_end_y - bound_start_y),
196 };
197
198 return {
199 .average_intensity = average_intensity,
200 .centroid = centroid,
201 .pixel_count = static_cast<u32>(pixel_count),
202 .bound = bound,
203 };
204}
205
206u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
207 if ((y * width) + x >= data.size()) {
208 return 0;
209 }
210 return data[(y * width) + x];
211}
212
213void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
214 if ((y * width) + x >= data.size()) {
215 return;
216 }
217 data[(y * width) + x] = value;
218}
219
220void ClusteringProcessor::SetDefaultConfig() {
221 using namespace std::literals::chrono_literals;
222 current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
223 current_config.camera_config.gain = 2;
224 current_config.camera_config.is_negative_used = false;
225 current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
226 current_config.window_of_interest = {
227 .x = 0,
228 .y = 0,
229 .width = width,
230 .height = height,
231 };
232 current_config.pixel_count_min = 3;
233 current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
234 current_config.is_external_light_filter_enabled = true;
235 current_config.object_intensity_min = 150;
236
237 npad_device->SetCameraFormat(format);
238}
239
240void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
241 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
242 current_config.camera_config.gain = config.camera_config.gain;
243 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
244 current_config.camera_config.light_target =
245 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
246 current_config.window_of_interest = config.window_of_interest;
247 current_config.pixel_count_min = config.pixel_count_min;
248 current_config.pixel_count_max = config.pixel_count_max;
249 current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
250 current_config.object_intensity_min = config.object_intensity_min;
251
252 LOG_INFO(Service_IRS,
253 "Processor config, exposure_time={}, gain={}, is_negative_used={}, "
254 "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
255 "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
256 current_config.camera_config.exposure_time, current_config.camera_config.gain,
257 current_config.camera_config.is_negative_used,
258 current_config.camera_config.light_target, current_config.window_of_interest.x,
259 current_config.window_of_interest.y, current_config.window_of_interest.width,
260 current_config.window_of_interest.height, current_config.pixel_count_min,
261 current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
262 current_config.object_intensity_min);
263
264 npad_device->SetCameraFormat(format);
265}
266
267} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/clustering_processor.h b/src/hid_core/irsensor/clustering_processor.h
new file mode 100644
index 000000000..e3b60d9b0
--- /dev/null
+++ b/src/hid_core/irsensor/clustering_processor.h
@@ -0,0 +1,115 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "hid_core/irsensor/irs_types.h"
8#include "hid_core/irsensor/processor_base.h"
9#include "hid_core/resources/irs_ring_lifo.h"
10
11namespace Core {
12class System;
13}
14
15namespace Core::HID {
16class EmulatedController;
17} // namespace Core::HID
18
19namespace Service::IRS {
20class ClusteringProcessor final : public ProcessorBase {
21public:
22 explicit ClusteringProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
23 std::size_t npad_index);
24 ~ClusteringProcessor() override;
25
26 // Called when the processor is initialized
27 void StartProcessor() override;
28
29 // Called when the processor is suspended
30 void SuspendProcessor() override;
31
32 // Called when the processor is stopped
33 void StopProcessor() override;
34
35 // Sets config parameters of the camera
36 void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
37
38private:
39 static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
40 static constexpr std::size_t width = 320;
41 static constexpr std::size_t height = 240;
42
43 // This is nn::irsensor::ClusteringProcessorConfig
44 struct ClusteringProcessorConfig {
45 Core::IrSensor::CameraConfig camera_config;
46 Core::IrSensor::IrsRect window_of_interest;
47 u32 pixel_count_min;
48 u32 pixel_count_max;
49 u32 object_intensity_min;
50 bool is_external_light_filter_enabled;
51 INSERT_PADDING_BYTES(3);
52 };
53 static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
54 "ClusteringProcessorConfig is an invalid size");
55
56 // This is nn::irsensor::AdaptiveClusteringProcessorConfig
57 struct AdaptiveClusteringProcessorConfig {
58 Core::IrSensor::AdaptiveClusteringMode mode;
59 Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
60 };
61 static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
62 "AdaptiveClusteringProcessorConfig is an invalid size");
63
64 // This is nn::irsensor::ClusteringData
65 struct ClusteringData {
66 f32 average_intensity;
67 Core::IrSensor::IrsCentroid centroid;
68 u32 pixel_count;
69 Core::IrSensor::IrsRect bound;
70 };
71 static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
72
73 // This is nn::irsensor::ClusteringProcessorState
74 struct ClusteringProcessorState {
75 s64 sampling_number;
76 u64 timestamp;
77 u8 object_count;
78 INSERT_PADDING_BYTES(3);
79 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
80 std::array<ClusteringData, 0x10> data;
81 };
82 static_assert(sizeof(ClusteringProcessorState) == 0x198,
83 "ClusteringProcessorState is an invalid size");
84
85 struct ClusteringSharedMemory {
86 Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
87 static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
88 INSERT_PADDING_WORDS(0x11F);
89 };
90 static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
91 "ClusteringSharedMemory is an invalid size");
92
93 void OnControllerUpdate(Core::HID::ControllerTriggerType type);
94 void RemoveLowIntensityData(std::vector<u8>& data);
95 ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
96 ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
97 std::size_t y) const;
98 ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
99 u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
100 void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
101
102 // Sets config parameters of the camera
103 void SetDefaultConfig();
104
105 ClusteringSharedMemory* shared_memory = nullptr;
106 ClusteringProcessorState next_state{};
107
108 ClusteringProcessorConfig current_config{};
109 Core::IrSensor::DeviceFormat& device;
110 Core::HID::EmulatedController* npad_device;
111 int callback_key{};
112
113 Core::System& system;
114};
115} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/image_transfer_processor.cpp b/src/hid_core/irsensor/image_transfer_processor.cpp
new file mode 100644
index 000000000..d6573f8dc
--- /dev/null
+++ b/src/hid_core/irsensor/image_transfer_processor.cpp
@@ -0,0 +1,155 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/core.h"
5#include "core/memory.h"
6#include "hid_core/frontend/emulated_controller.h"
7#include "hid_core/hid_core.h"
8#include "hid_core/irsensor/image_transfer_processor.h"
9
10namespace Service::IRS {
11ImageTransferProcessor::ImageTransferProcessor(Core::System& system_,
12 Core::IrSensor::DeviceFormat& device_format,
13 std::size_t npad_index)
14 : device{device_format}, system{system_} {
15 npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
16
17 Core::HID::ControllerUpdateCallback engine_callback{
18 .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
19 .is_npad_service = true,
20 };
21 callback_key = npad_device->SetCallback(engine_callback);
22
23 device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
24 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
25 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
26}
27
28ImageTransferProcessor::~ImageTransferProcessor() {
29 npad_device->DeleteCallback(callback_key);
30};
31
32void ImageTransferProcessor::StartProcessor() {
33 is_active = true;
34 device.camera_status = Core::IrSensor::IrCameraStatus::Available;
35 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
36 processor_state.sampling_number = 0;
37 processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
38}
39
40void ImageTransferProcessor::SuspendProcessor() {}
41
42void ImageTransferProcessor::StopProcessor() {}
43
44void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
45 if (type != Core::HID::ControllerTriggerType::IrSensor) {
46 return;
47 }
48 if (transfer_memory == 0) {
49 return;
50 }
51
52 const auto& camera_data = npad_device->GetCamera();
53
54 // This indicates how much ambient light is present
55 processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
56 processor_state.sampling_number = camera_data.sample;
57
58 if (camera_data.format != current_config.origin_format) {
59 LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
60 current_config.origin_format);
61 system.ApplicationMemory().ZeroBlock(transfer_memory,
62 GetDataSize(current_config.trimming_format));
63 return;
64 }
65
66 if (current_config.origin_format > current_config.trimming_format) {
67 LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
68 current_config.origin_format, current_config.trimming_format);
69 system.ApplicationMemory().ZeroBlock(transfer_memory,
70 GetDataSize(current_config.trimming_format));
71 return;
72 }
73
74 std::vector<u8> window_data{};
75 const auto origin_width = GetDataWidth(current_config.origin_format);
76 const auto origin_height = GetDataHeight(current_config.origin_format);
77 const auto trimming_width = GetDataWidth(current_config.trimming_format);
78 const auto trimming_height = GetDataHeight(current_config.trimming_format);
79 window_data.resize(GetDataSize(current_config.trimming_format));
80
81 if (trimming_width + current_config.trimming_start_x > origin_width ||
82 trimming_height + current_config.trimming_start_y > origin_height) {
83 LOG_WARNING(Service_IRS,
84 "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
85 current_config.trimming_start_x, current_config.trimming_start_y,
86 trimming_width, trimming_height, origin_width, origin_height);
87 system.ApplicationMemory().ZeroBlock(transfer_memory,
88 GetDataSize(current_config.trimming_format));
89 return;
90 }
91
92 for (std::size_t y = 0; y < trimming_height; y++) {
93 for (std::size_t x = 0; x < trimming_width; x++) {
94 const std::size_t window_index = (y * trimming_width) + x;
95 const std::size_t origin_index =
96 ((y + current_config.trimming_start_y) * origin_width) + x +
97 current_config.trimming_start_x;
98 window_data[window_index] = camera_data.data[origin_index];
99 }
100 }
101
102 system.ApplicationMemory().WriteBlock(transfer_memory, window_data.data(),
103 GetDataSize(current_config.trimming_format));
104
105 if (!IsProcessorActive()) {
106 StartProcessor();
107 }
108}
109
110void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
111 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
112 current_config.camera_config.gain = config.camera_config.gain;
113 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
114 current_config.camera_config.light_target =
115 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
116 current_config.origin_format =
117 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
118 current_config.trimming_format =
119 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
120 current_config.trimming_start_x = 0;
121 current_config.trimming_start_y = 0;
122
123 npad_device->SetCameraFormat(current_config.origin_format);
124}
125
126void ImageTransferProcessor::SetConfig(
127 Core::IrSensor::PackedImageTransferProcessorExConfig config) {
128 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
129 current_config.camera_config.gain = config.camera_config.gain;
130 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
131 current_config.camera_config.light_target =
132 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
133 current_config.origin_format =
134 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
135 current_config.trimming_format =
136 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
137 current_config.trimming_start_x = config.trimming_start_x;
138 current_config.trimming_start_y = config.trimming_start_y;
139
140 npad_device->SetCameraFormat(current_config.origin_format);
141}
142
143void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
144 transfer_memory = t_mem;
145}
146
147Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
148 std::vector<u8>& data) const {
149 const auto size = GetDataSize(current_config.trimming_format);
150 data.resize(size);
151 system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size);
152 return processor_state;
153}
154
155} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/image_transfer_processor.h b/src/hid_core/irsensor/image_transfer_processor.h
new file mode 100644
index 000000000..4e0117084
--- /dev/null
+++ b/src/hid_core/irsensor/image_transfer_processor.h
@@ -0,0 +1,77 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/typed_address.h"
7#include "hid_core/irsensor/irs_types.h"
8#include "hid_core/irsensor/processor_base.h"
9
10namespace Core {
11class System;
12}
13
14namespace Core::HID {
15class EmulatedController;
16} // namespace Core::HID
17
18namespace Service::IRS {
19class ImageTransferProcessor final : public ProcessorBase {
20public:
21 explicit ImageTransferProcessor(Core::System& system_,
22 Core::IrSensor::DeviceFormat& device_format,
23 std::size_t npad_index);
24 ~ImageTransferProcessor() override;
25
26 // Called when the processor is initialized
27 void StartProcessor() override;
28
29 // Called when the processor is suspended
30 void SuspendProcessor() override;
31
32 // Called when the processor is stopped
33 void StopProcessor() override;
34
35 // Sets config parameters of the camera
36 void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
37 void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
38
39 // Transfer memory where the image data will be stored
40 void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
41
42 Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
43
44private:
45 // This is nn::irsensor::ImageTransferProcessorConfig
46 struct ImageTransferProcessorConfig {
47 Core::IrSensor::CameraConfig camera_config;
48 Core::IrSensor::ImageTransferProcessorFormat format;
49 };
50 static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
51 "ImageTransferProcessorConfig is an invalid size");
52
53 // This is nn::irsensor::ImageTransferProcessorExConfig
54 struct ImageTransferProcessorExConfig {
55 Core::IrSensor::CameraConfig camera_config;
56 Core::IrSensor::ImageTransferProcessorFormat origin_format;
57 Core::IrSensor::ImageTransferProcessorFormat trimming_format;
58 u16 trimming_start_x;
59 u16 trimming_start_y;
60 bool is_external_light_filter_enabled;
61 INSERT_PADDING_BYTES(3);
62 };
63 static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
64 "ImageTransferProcessorExConfig is an invalid size");
65
66 void OnControllerUpdate(Core::HID::ControllerTriggerType type);
67
68 ImageTransferProcessorExConfig current_config{};
69 Core::IrSensor::ImageTransferProcessorState processor_state{};
70 Core::IrSensor::DeviceFormat& device;
71 Core::HID::EmulatedController* npad_device;
72 int callback_key{};
73
74 Core::System& system;
75 Common::ProcessAddress transfer_memory{};
76};
77} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/ir_led_processor.cpp b/src/hid_core/irsensor/ir_led_processor.cpp
new file mode 100644
index 000000000..4b04e05b5
--- /dev/null
+++ b/src/hid_core/irsensor/ir_led_processor.cpp
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/irsensor/ir_led_processor.h"
5
6namespace Service::IRS {
7IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
8 : device(device_format) {
9 device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
12}
13
14IrLedProcessor::~IrLedProcessor() = default;
15
16void IrLedProcessor::StartProcessor() {}
17
18void IrLedProcessor::SuspendProcessor() {}
19
20void IrLedProcessor::StopProcessor() {}
21
22void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
23 current_config.light_target =
24 static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
25}
26
27} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/ir_led_processor.h b/src/hid_core/irsensor/ir_led_processor.h
new file mode 100644
index 000000000..03d0c4245
--- /dev/null
+++ b/src/hid_core/irsensor/ir_led_processor.h
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "hid_core/irsensor/irs_types.h"
9#include "hid_core/irsensor/processor_base.h"
10
11namespace Service::IRS {
12class IrLedProcessor final : public ProcessorBase {
13public:
14 explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
15 ~IrLedProcessor() override;
16
17 // Called when the processor is initialized
18 void StartProcessor() override;
19
20 // Called when the processor is suspended
21 void SuspendProcessor() override;
22
23 // Called when the processor is stopped
24 void StopProcessor() override;
25
26 // Sets config parameters of the camera
27 void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
28
29private:
30 // This is nn::irsensor::IrLedProcessorConfig
31 struct IrLedProcessorConfig {
32 Core::IrSensor::CameraLightTarget light_target;
33 };
34 static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
35
36 struct IrLedProcessorState {
37 s64 sampling_number;
38 u64 timestamp;
39 std::array<u8, 0x8> data;
40 };
41 static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
42
43 IrLedProcessorConfig current_config{};
44 Core::IrSensor::DeviceFormat& device;
45};
46
47} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/irs_types.h b/src/hid_core/irsensor/irs_types.h
new file mode 100644
index 000000000..017f38e6c
--- /dev/null
+++ b/src/hid_core/irsensor/irs_types.h
@@ -0,0 +1,301 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8#include "hid_core/hid_types.h"
9
10namespace Core::IrSensor {
11
12// This is nn::irsensor::CameraAmbientNoiseLevel
13enum class CameraAmbientNoiseLevel : u32 {
14 Low,
15 Medium,
16 High,
17 Unknown3, // This level can't be reached
18};
19
20// This is nn::irsensor::CameraLightTarget
21enum class CameraLightTarget : u32 {
22 AllLeds,
23 BrightLeds,
24 DimLeds,
25 None,
26};
27
28// This is nn::irsensor::PackedCameraLightTarget
29enum class PackedCameraLightTarget : u8 {
30 AllLeds,
31 BrightLeds,
32 DimLeds,
33 None,
34};
35
36// This is nn::irsensor::AdaptiveClusteringMode
37enum class AdaptiveClusteringMode : u32 {
38 StaticFov,
39 DynamicFov,
40};
41
42// This is nn::irsensor::AdaptiveClusteringTargetDistance
43enum class AdaptiveClusteringTargetDistance : u32 {
44 Near,
45 Middle,
46 Far,
47};
48
49// This is nn::irsensor::ImageTransferProcessorFormat
50enum class ImageTransferProcessorFormat : u32 {
51 Size320x240,
52 Size160x120,
53 Size80x60,
54 Size40x30,
55 Size20x15,
56};
57
58// This is nn::irsensor::PackedImageTransferProcessorFormat
59enum class PackedImageTransferProcessorFormat : u8 {
60 Size320x240,
61 Size160x120,
62 Size80x60,
63 Size40x30,
64 Size20x15,
65};
66
67// This is nn::irsensor::IrCameraStatus
68enum class IrCameraStatus : u32 {
69 Available,
70 Unsupported,
71 Unconnected,
72};
73
74// This is nn::irsensor::IrCameraInternalStatus
75enum class IrCameraInternalStatus : u32 {
76 Stopped,
77 FirmwareUpdateNeeded,
78 Unknown2,
79 Unknown3,
80 Unknown4,
81 FirmwareVersionRequested,
82 FirmwareVersionIsInvalid,
83 Ready,
84 Setting,
85};
86
87// This is nn::irsensor::detail::StatusManager::IrSensorMode
88enum class IrSensorMode : u64 {
89 None,
90 MomentProcessor,
91 ClusteringProcessor,
92 ImageTransferProcessor,
93 PointingProcessorMarker,
94 TeraPluginProcessor,
95 IrLedProcessor,
96};
97
98// This is nn::irsensor::ImageProcessorStatus
99enum ImageProcessorStatus : u32 {
100 Stopped,
101 Running,
102};
103
104// This is nn::irsensor::HandAnalysisMode
105enum class HandAnalysisMode : u32 {
106 None,
107 Silhouette,
108 Image,
109 SilhoueteAndImage,
110 SilhuetteOnly,
111};
112
113// This is nn::irsensor::IrSensorFunctionLevel
114enum class IrSensorFunctionLevel : u8 {
115 unknown0,
116 unknown1,
117 unknown2,
118 unknown3,
119 unknown4,
120};
121
122// This is nn::irsensor::MomentProcessorPreprocess
123enum class MomentProcessorPreprocess : u32 {
124 Unknown0,
125 Unknown1,
126};
127
128// This is nn::irsensor::PackedMomentProcessorPreprocess
129enum class PackedMomentProcessorPreprocess : u8 {
130 Unknown0,
131 Unknown1,
132};
133
134// This is nn::irsensor::PointingStatus
135enum class PointingStatus : u32 {
136 Unknown0,
137 Unknown1,
138};
139
140struct IrsRect {
141 s16 x;
142 s16 y;
143 s16 width;
144 s16 height;
145};
146
147struct IrsCentroid {
148 f32 x;
149 f32 y;
150};
151
152struct CameraConfig {
153 u64 exposure_time;
154 CameraLightTarget light_target;
155 u32 gain;
156 bool is_negative_used;
157 INSERT_PADDING_BYTES(7);
158};
159static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
160
161struct PackedCameraConfig {
162 u64 exposure_time;
163 PackedCameraLightTarget light_target;
164 u8 gain;
165 bool is_negative_used;
166 INSERT_PADDING_BYTES(5);
167};
168static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
169
170// This is nn::irsensor::IrCameraHandle
171struct IrCameraHandle {
172 u8 npad_id{};
173 Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
174 INSERT_PADDING_BYTES(2);
175};
176static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
177
178// This is nn::irsensor::PackedMcuVersion
179struct PackedMcuVersion {
180 u16 major;
181 u16 minor;
182};
183static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
184
185// This is nn::irsensor::PackedMomentProcessorConfig
186struct PackedMomentProcessorConfig {
187 PackedCameraConfig camera_config;
188 IrsRect window_of_interest;
189 PackedMcuVersion required_mcu_version;
190 PackedMomentProcessorPreprocess preprocess;
191 u8 preprocess_intensity_threshold;
192 INSERT_PADDING_BYTES(2);
193};
194static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
195 "PackedMomentProcessorConfig is an invalid size");
196
197// This is nn::irsensor::PackedClusteringProcessorConfig
198struct PackedClusteringProcessorConfig {
199 PackedCameraConfig camera_config;
200 IrsRect window_of_interest;
201 PackedMcuVersion required_mcu_version;
202 u32 pixel_count_min;
203 u32 pixel_count_max;
204 u8 object_intensity_min;
205 bool is_external_light_filter_enabled;
206 INSERT_PADDING_BYTES(2);
207};
208static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
209 "PackedClusteringProcessorConfig is an invalid size");
210
211// This is nn::irsensor::PackedImageTransferProcessorConfig
212struct PackedImageTransferProcessorConfig {
213 PackedCameraConfig camera_config;
214 PackedMcuVersion required_mcu_version;
215 PackedImageTransferProcessorFormat format;
216 INSERT_PADDING_BYTES(3);
217};
218static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
219 "PackedImageTransferProcessorConfig is an invalid size");
220
221// This is nn::irsensor::PackedTeraPluginProcessorConfig
222struct PackedTeraPluginProcessorConfig {
223 PackedMcuVersion required_mcu_version;
224 u8 mode;
225 u8 unknown_1;
226 u8 unknown_2;
227 u8 unknown_3;
228};
229static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
230 "PackedTeraPluginProcessorConfig is an invalid size");
231
232// This is nn::irsensor::PackedPointingProcessorConfig
233struct PackedPointingProcessorConfig {
234 IrsRect window_of_interest;
235 PackedMcuVersion required_mcu_version;
236};
237static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
238 "PackedPointingProcessorConfig is an invalid size");
239
240// This is nn::irsensor::PackedFunctionLevel
241struct PackedFunctionLevel {
242 IrSensorFunctionLevel function_level;
243 INSERT_PADDING_BYTES(3);
244};
245static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
246
247// This is nn::irsensor::PackedImageTransferProcessorExConfig
248struct PackedImageTransferProcessorExConfig {
249 PackedCameraConfig camera_config;
250 PackedMcuVersion required_mcu_version;
251 PackedImageTransferProcessorFormat origin_format;
252 PackedImageTransferProcessorFormat trimming_format;
253 u16 trimming_start_x;
254 u16 trimming_start_y;
255 bool is_external_light_filter_enabled;
256 INSERT_PADDING_BYTES(5);
257};
258static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
259 "PackedImageTransferProcessorExConfig is an invalid size");
260
261// This is nn::irsensor::PackedIrLedProcessorConfig
262struct PackedIrLedProcessorConfig {
263 PackedMcuVersion required_mcu_version;
264 u8 light_target;
265 INSERT_PADDING_BYTES(3);
266};
267static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
268 "PackedIrLedProcessorConfig is an invalid size");
269
270// This is nn::irsensor::HandAnalysisConfig
271struct HandAnalysisConfig {
272 HandAnalysisMode mode;
273};
274static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
275
276// This is nn::irsensor::detail::ProcessorState contents are different for each processor
277struct ProcessorState {
278 std::array<u8, 0xE20> processor_raw_data{};
279};
280static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
281
282// This is nn::irsensor::detail::DeviceFormat
283struct DeviceFormat {
284 Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
285 Core::IrSensor::IrCameraInternalStatus camera_internal_status{
286 Core::IrSensor::IrCameraInternalStatus::Ready};
287 Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
288 ProcessorState state{};
289};
290static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
291
292// This is nn::irsensor::ImageTransferProcessorState
293struct ImageTransferProcessorState {
294 u64 sampling_number;
295 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
296 INSERT_PADDING_BYTES(4);
297};
298static_assert(sizeof(ImageTransferProcessorState) == 0x10,
299 "ImageTransferProcessorState is an invalid size");
300
301} // namespace Core::IrSensor
diff --git a/src/hid_core/irsensor/moment_processor.cpp b/src/hid_core/irsensor/moment_processor.cpp
new file mode 100644
index 000000000..0284a58bd
--- /dev/null
+++ b/src/hid_core/irsensor/moment_processor.cpp
@@ -0,0 +1,149 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "hid_core/frontend/emulated_controller.h"
7#include "hid_core/hid_core.h"
8#include "hid_core/irsensor/moment_processor.h"
9
10namespace Service::IRS {
11static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30;
12static constexpr std::size_t ImageWidth = 40;
13static constexpr std::size_t ImageHeight = 30;
14
15MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
16 std::size_t npad_index)
17 : device(device_format), system{system_} {
18 npad_device = system.HIDCore().GetEmulatedControllerByIndex(npad_index);
19
20 device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
21 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
22 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
23
24 shared_memory = std::construct_at(
25 reinterpret_cast<MomentSharedMemory*>(&device_format.state.processor_raw_data));
26
27 Core::HID::ControllerUpdateCallback engine_callback{
28 .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
29 .is_npad_service = true,
30 };
31 callback_key = npad_device->SetCallback(engine_callback);
32}
33
34MomentProcessor::~MomentProcessor() {
35 npad_device->DeleteCallback(callback_key);
36};
37
38void MomentProcessor::StartProcessor() {
39 device.camera_status = Core::IrSensor::IrCameraStatus::Available;
40 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
41}
42
43void MomentProcessor::SuspendProcessor() {}
44
45void MomentProcessor::StopProcessor() {}
46
47void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
48 if (type != Core::HID::ControllerTriggerType::IrSensor) {
49 return;
50 }
51
52 next_state = {};
53 const auto& camera_data = npad_device->GetCamera();
54
55 const auto window_width = static_cast<std::size_t>(current_config.window_of_interest.width);
56 const auto window_height = static_cast<std::size_t>(current_config.window_of_interest.height);
57 const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
58 const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
59
60 const std::size_t block_width = window_width / Columns;
61 const std::size_t block_height = window_height / Rows;
62
63 for (std::size_t row = 0; row < Rows; row++) {
64 for (std::size_t column = 0; column < Columns; column++) {
65 const size_t x_pos = (column * block_width) + window_start_x;
66 const size_t y_pos = (row * block_height) + window_start_y;
67 auto& statistic = next_state.statistic[column + (row * Columns)];
68 statistic = GetStatistic(camera_data.data, x_pos, y_pos, block_width, block_height);
69 }
70 }
71
72 next_state.sampling_number = camera_data.sample;
73 next_state.timestamp = system.CoreTiming().GetGlobalTimeNs().count();
74 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
75 shared_memory->moment_lifo.WriteNextEntry(next_state);
76
77 if (!IsProcessorActive()) {
78 StartProcessor();
79 }
80}
81
82u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
83 if ((y * ImageWidth) + x >= data.size()) {
84 return 0;
85 }
86 return data[(y * ImageWidth) + x];
87}
88
89MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector<u8>& data,
90 std::size_t start_x,
91 std::size_t start_y,
92 std::size_t width,
93 std::size_t height) const {
94 // The actual implementation is always 320x240
95 static constexpr std::size_t RealWidth = 320;
96 static constexpr std::size_t RealHeight = 240;
97 static constexpr std::size_t Threshold = 30;
98 MomentStatistic statistic{};
99 std::size_t active_points{};
100
101 // Sum all data points on the block that meet with the threshold
102 for (std::size_t y = 0; y < width; y++) {
103 for (std::size_t x = 0; x < height; x++) {
104 const size_t x_pos = x + start_x;
105 const size_t y_pos = y + start_y;
106 const auto pixel =
107 GetPixel(data, x_pos * ImageWidth / RealWidth, y_pos * ImageHeight / RealHeight);
108
109 if (pixel < Threshold) {
110 continue;
111 }
112
113 statistic.average_intensity += pixel;
114
115 statistic.centroid.x += static_cast<float>(x_pos);
116 statistic.centroid.y += static_cast<float>(y_pos);
117
118 active_points++;
119 }
120 }
121
122 // Return an empty field if no points were available
123 if (active_points == 0) {
124 return {};
125 }
126
127 // Finally calculate the actual centroid and average intensity
128 statistic.centroid.x /= static_cast<float>(active_points);
129 statistic.centroid.y /= static_cast<float>(active_points);
130 statistic.average_intensity /= static_cast<f32>(width * height);
131
132 return statistic;
133}
134
135void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
136 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
137 current_config.camera_config.gain = config.camera_config.gain;
138 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
139 current_config.camera_config.light_target =
140 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
141 current_config.window_of_interest = config.window_of_interest;
142 current_config.preprocess =
143 static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
144 current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
145
146 npad_device->SetCameraFormat(format);
147}
148
149} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/moment_processor.h b/src/hid_core/irsensor/moment_processor.h
new file mode 100644
index 000000000..78c9c035f
--- /dev/null
+++ b/src/hid_core/irsensor/moment_processor.h
@@ -0,0 +1,91 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "hid_core/irsensor/irs_types.h"
9#include "hid_core/irsensor/processor_base.h"
10#include "hid_core/resources/irs_ring_lifo.h"
11
12namespace Core {
13class System;
14}
15
16namespace Core::HID {
17class EmulatedController;
18} // namespace Core::HID
19
20namespace Service::IRS {
21class MomentProcessor final : public ProcessorBase {
22public:
23 explicit MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
24 std::size_t npad_index);
25 ~MomentProcessor() override;
26
27 // Called when the processor is initialized
28 void StartProcessor() override;
29
30 // Called when the processor is suspended
31 void SuspendProcessor() override;
32
33 // Called when the processor is stopped
34 void StopProcessor() override;
35
36 // Sets config parameters of the camera
37 void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
38
39private:
40 static constexpr std::size_t Columns = 8;
41 static constexpr std::size_t Rows = 6;
42
43 // This is nn::irsensor::MomentProcessorConfig
44 struct MomentProcessorConfig {
45 Core::IrSensor::CameraConfig camera_config;
46 Core::IrSensor::IrsRect window_of_interest;
47 Core::IrSensor::MomentProcessorPreprocess preprocess;
48 u32 preprocess_intensity_threshold;
49 };
50 static_assert(sizeof(MomentProcessorConfig) == 0x28,
51 "MomentProcessorConfig is an invalid size");
52
53 // This is nn::irsensor::MomentStatistic
54 struct MomentStatistic {
55 f32 average_intensity;
56 Core::IrSensor::IrsCentroid centroid;
57 };
58 static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
59
60 // This is nn::irsensor::MomentProcessorState
61 struct MomentProcessorState {
62 s64 sampling_number;
63 u64 timestamp;
64 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
65 INSERT_PADDING_BYTES(4);
66 std::array<MomentStatistic, Columns * Rows> statistic;
67 };
68 static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
69
70 struct MomentSharedMemory {
71 Service::IRS::Lifo<MomentProcessorState, 6> moment_lifo;
72 };
73 static_assert(sizeof(MomentSharedMemory) == 0xE20, "MomentSharedMemory is an invalid size");
74
75 void OnControllerUpdate(Core::HID::ControllerTriggerType type);
76 u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
77 MomentStatistic GetStatistic(const std::vector<u8>& data, std::size_t start_x,
78 std::size_t start_y, std::size_t width, std::size_t height) const;
79
80 MomentSharedMemory* shared_memory = nullptr;
81 MomentProcessorState next_state{};
82
83 MomentProcessorConfig current_config{};
84 Core::IrSensor::DeviceFormat& device;
85 Core::HID::EmulatedController* npad_device;
86 int callback_key{};
87
88 Core::System& system;
89};
90
91} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/pointing_processor.cpp b/src/hid_core/irsensor/pointing_processor.cpp
new file mode 100644
index 000000000..c1d6c1bb6
--- /dev/null
+++ b/src/hid_core/irsensor/pointing_processor.cpp
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/irsensor/pointing_processor.h"
5
6namespace Service::IRS {
7PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
8 : device(device_format) {
9 device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
12}
13
14PointingProcessor::~PointingProcessor() = default;
15
16void PointingProcessor::StartProcessor() {}
17
18void PointingProcessor::SuspendProcessor() {}
19
20void PointingProcessor::StopProcessor() {}
21
22void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
23 current_config.window_of_interest = config.window_of_interest;
24}
25
26} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/pointing_processor.h b/src/hid_core/irsensor/pointing_processor.h
new file mode 100644
index 000000000..968c2e5bd
--- /dev/null
+++ b/src/hid_core/irsensor/pointing_processor.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "hid_core/irsensor/irs_types.h"
8#include "hid_core/irsensor/processor_base.h"
9
10namespace Service::IRS {
11class PointingProcessor final : public ProcessorBase {
12public:
13 explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
14 ~PointingProcessor() override;
15
16 // Called when the processor is initialized
17 void StartProcessor() override;
18
19 // Called when the processor is suspended
20 void SuspendProcessor() override;
21
22 // Called when the processor is stopped
23 void StopProcessor() override;
24
25 // Sets config parameters of the camera
26 void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
27
28private:
29 // This is nn::irsensor::PointingProcessorConfig
30 struct PointingProcessorConfig {
31 Core::IrSensor::IrsRect window_of_interest;
32 };
33 static_assert(sizeof(PointingProcessorConfig) == 0x8,
34 "PointingProcessorConfig is an invalid size");
35
36 struct PointingProcessorMarkerData {
37 u8 pointing_status;
38 INSERT_PADDING_BYTES(3);
39 u32 unknown;
40 float unknown_float1;
41 float position_x;
42 float position_y;
43 float unknown_float2;
44 Core::IrSensor::IrsRect window_of_interest;
45 };
46 static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
47 "PointingProcessorMarkerData is an invalid size");
48
49 struct PointingProcessorMarkerState {
50 s64 sampling_number;
51 u64 timestamp;
52 std::array<PointingProcessorMarkerData, 0x3> data;
53 };
54 static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
55 "PointingProcessorMarkerState is an invalid size");
56
57 PointingProcessorConfig current_config{};
58 Core::IrSensor::DeviceFormat& device;
59};
60
61} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/processor_base.cpp b/src/hid_core/irsensor/processor_base.cpp
new file mode 100644
index 000000000..91a513a70
--- /dev/null
+++ b/src/hid_core/irsensor/processor_base.cpp
@@ -0,0 +1,67 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/irsensor/processor_base.h"
5
6namespace Service::IRS {
7
8ProcessorBase::ProcessorBase() {}
9ProcessorBase::~ProcessorBase() = default;
10
11bool ProcessorBase::IsProcessorActive() const {
12 return is_active;
13}
14
15std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
16 switch (format) {
17 case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
18 return 320 * 240;
19 case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
20 return 160 * 120;
21 case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
22 return 80 * 60;
23 case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
24 return 40 * 30;
25 case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
26 return 20 * 15;
27 default:
28 return 0;
29 }
30}
31
32std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
33 switch (format) {
34 case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
35 return 320;
36 case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
37 return 160;
38 case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
39 return 80;
40 case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
41 return 40;
42 case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
43 return 20;
44 default:
45 return 0;
46 }
47}
48
49std::size_t ProcessorBase::GetDataHeight(
50 Core::IrSensor::ImageTransferProcessorFormat format) const {
51 switch (format) {
52 case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
53 return 240;
54 case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
55 return 120;
56 case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
57 return 60;
58 case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
59 return 30;
60 case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
61 return 15;
62 default:
63 return 0;
64 }
65}
66
67} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/processor_base.h b/src/hid_core/irsensor/processor_base.h
new file mode 100644
index 000000000..48beeafba
--- /dev/null
+++ b/src/hid_core/irsensor/processor_base.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "hid_core/irsensor/irs_types.h"
8
9namespace Service::IRS {
10class ProcessorBase {
11public:
12 explicit ProcessorBase();
13 virtual ~ProcessorBase();
14
15 virtual void StartProcessor() = 0;
16 virtual void SuspendProcessor() = 0;
17 virtual void StopProcessor() = 0;
18
19 bool IsProcessorActive() const;
20
21protected:
22 /// Returns the number of bytes the image uses
23 std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
24
25 /// Returns the width of the image
26 std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
27
28 /// Returns the height of the image
29 std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
30
31 bool is_active{false};
32};
33} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/tera_plugin_processor.cpp b/src/hid_core/irsensor/tera_plugin_processor.cpp
new file mode 100644
index 000000000..2382e208a
--- /dev/null
+++ b/src/hid_core/irsensor/tera_plugin_processor.cpp
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "hid_core/irsensor/tera_plugin_processor.h"
5
6namespace Service::IRS {
7TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
8 : device(device_format) {
9 device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
12}
13
14TeraPluginProcessor::~TeraPluginProcessor() = default;
15
16void TeraPluginProcessor::StartProcessor() {}
17
18void TeraPluginProcessor::SuspendProcessor() {}
19
20void TeraPluginProcessor::StopProcessor() {}
21
22void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
23 current_config.mode = config.mode;
24 current_config.unknown_1 = config.unknown_1;
25 current_config.unknown_2 = config.unknown_2;
26 current_config.unknown_3 = config.unknown_3;
27}
28
29} // namespace Service::IRS
diff --git a/src/hid_core/irsensor/tera_plugin_processor.h b/src/hid_core/irsensor/tera_plugin_processor.h
new file mode 100644
index 000000000..dc8fe7d07
--- /dev/null
+++ b/src/hid_core/irsensor/tera_plugin_processor.h
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "hid_core/irsensor/irs_types.h"
9#include "hid_core/irsensor/processor_base.h"
10
11namespace Service::IRS {
12class TeraPluginProcessor final : public ProcessorBase {
13public:
14 explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
15 ~TeraPluginProcessor() override;
16
17 // Called when the processor is initialized
18 void StartProcessor() override;
19
20 // Called when the processor is suspended
21 void SuspendProcessor() override;
22
23 // Called when the processor is stopped
24 void StopProcessor() override;
25
26 // Sets config parameters of the camera
27 void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
28
29private:
30 // This is nn::irsensor::TeraPluginProcessorConfig
31 struct TeraPluginProcessorConfig {
32 u8 mode;
33 u8 unknown_1;
34 u8 unknown_2;
35 u8 unknown_3;
36 };
37 static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
38 "TeraPluginProcessorConfig is an invalid size");
39
40 struct TeraPluginProcessorState {
41 s64 sampling_number;
42 u64 timestamp;
43 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
44 std::array<u8, 0x12c> data;
45 };
46 static_assert(sizeof(TeraPluginProcessorState) == 0x140,
47 "TeraPluginProcessorState is an invalid size");
48
49 TeraPluginProcessorConfig current_config{};
50 Core::IrSensor::DeviceFormat& device;
51};
52
53} // namespace Service::IRS