summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar german772022-06-19 15:54:21 -0500
committerGravatar german772022-07-24 15:01:43 -0500
commit3ac4f3a2521e421a625fe1d7dcba05b8441fe056 (patch)
treece7e1f5be8624a8b6d3084b9c4ef522f87ba6ac7
parentMerge pull request #8484 from german77/irs_release (diff)
downloadyuzu-3ac4f3a2521e421a625fe1d7dcba05b8441fe056.tar.gz
yuzu-3ac4f3a2521e421a625fe1d7dcba05b8441fe056.tar.xz
yuzu-3ac4f3a2521e421a625fe1d7dcba05b8441fe056.zip
service: irs: Implement clustering processor
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/hle/service/hid/irs.cpp2
-rw-r--r--src/core/hle/service/hid/irs_ring_lifo.h47
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp237
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h38
-rw-r--r--src/yuzu/bootmanager.cpp2
6 files changed, 320 insertions, 7 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 32cc2f392..db7bc7a8b 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -446,6 +446,7 @@ add_library(core STATIC
446 hle/service/hid/hidbus.h 446 hle/service/hid/hidbus.h
447 hle/service/hid/irs.cpp 447 hle/service/hid/irs.cpp
448 hle/service/hid/irs.h 448 hle/service/hid/irs.h
449 hle/service/hid/irs_ring_lifo.h
449 hle/service/hid/ring_lifo.h 450 hle/service/hid/ring_lifo.h
450 hle/service/hid/xcd.cpp 451 hle/service/hid/xcd.cpp
451 hle/service/hid/xcd.h 452 hle/service/hid/xcd.h
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index d5107e41f..c4b44cbf9 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -166,7 +166,7 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
166 166
167 if (result.IsSuccess()) { 167 if (result.IsSuccess()) {
168 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); 168 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
169 MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device); 169 MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
170 auto& image_transfer_processor = 170 auto& image_transfer_processor =
171 GetProcessor<ClusteringProcessor>(parameters.camera_handle); 171 GetProcessor<ClusteringProcessor>(parameters.camera_handle);
172 image_transfer_processor.SetConfig(parameters.processor_config); 172 image_transfer_processor.SetConfig(parameters.processor_config);
diff --git a/src/core/hle/service/hid/irs_ring_lifo.h b/src/core/hle/service/hid/irs_ring_lifo.h
new file mode 100644
index 000000000..a31e61037
--- /dev/null
+++ b/src/core/hle/service/hid/irs_ring_lifo.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 <array>
7
8#include "common/common_types.h"
9
10namespace Service::IRS {
11
12template <typename State, std::size_t max_buffer_size>
13struct Lifo {
14 s64 sampling_number{};
15 s64 buffer_count{};
16 std::array<State, max_buffer_size> entries{};
17
18 const State& ReadCurrentEntry() const {
19 return entries[GetBufferTail()];
20 }
21
22 const State& ReadPreviousEntry() const {
23 return entries[GetPreviousEntryIndex()];
24 }
25
26 s64 GetBufferTail() const {
27 return sampling_number % max_buffer_size;
28 }
29
30 std::size_t GetPreviousEntryIndex() const {
31 return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
32 }
33
34 std::size_t GetNextEntryIndex() const {
35 return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
36 }
37
38 void WriteNextEntry(const State& new_state) {
39 if (buffer_count < max_buffer_size) {
40 buffer_count++;
41 }
42 sampling_number++;
43 entries[GetBufferTail()] = new_state;
44 }
45};
46
47} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
index 6479af212..57e1831b4 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -1,34 +1,263 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <queue>
5
6#include "core/hid/emulated_controller.h"
7#include "core/hid/hid_core.h"
4#include "core/hle/service/hid/irsensor/clustering_processor.h" 8#include "core/hle/service/hid/irsensor/clustering_processor.h"
5 9
6namespace Service::IRS { 10namespace Service::IRS {
7ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format) 11ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
8 : device(device_format) { 12 Core::IrSensor::DeviceFormat& device_format,
13 std::size_t npad_index)
14 : device{device_format} {
15 npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
16
9 device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; 17 device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; 18 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; 19 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
20 SetDefaultConfig();
21
22 shared_memory = std::construct_at(
23 reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
24
25 Core::HID::ControllerUpdateCallback engine_callback{
26 .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
27 .is_npad_service = true,
28 };
29 callback_key = npad_device->SetCallback(engine_callback);
12} 30}
13 31
14ClusteringProcessor::~ClusteringProcessor() = default; 32ClusteringProcessor::~ClusteringProcessor() {
33 npad_device->DeleteCallback(callback_key);
34};
15 35
16void ClusteringProcessor::StartProcessor() {} 36void ClusteringProcessor::StartProcessor() {
37 device.camera_status = Core::IrSensor::IrCameraStatus::Available;
38 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
39}
17 40
18void ClusteringProcessor::SuspendProcessor() {} 41void ClusteringProcessor::SuspendProcessor() {}
19 42
20void ClusteringProcessor::StopProcessor() {} 43void ClusteringProcessor::StopProcessor() {}
21 44
45void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
46 if (type != Core::HID::ControllerTriggerType::IrSensor) {
47 return;
48 }
49
50 next_state = {};
51 const auto camera_data = npad_device->GetCamera();
52 auto filtered_image = camera_data.data;
53
54 RemoveLowIntensityData(filtered_image);
55
56 const std::size_t window_start_x =
57 static_cast<std::size_t>(current_config.window_of_interest.x);
58 const std::size_t window_start_y =
59 static_cast<std::size_t>(current_config.window_of_interest.y);
60 const std::size_t window_end_x =
61 window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
62 const std::size_t 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 >= 0x10) {
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 = next_state.timestamp + 131;
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 std::queue<Common::Point<std::size_t>> search_points{};
109 ClusteringData current_cluster = GetPixelProperties(data, x, y);
110 SetPixel(data, x, y, 0);
111 search_points.push({x, y});
112
113 while (!search_points.empty()) {
114 const auto point = search_points.front();
115 search_points.pop();
116
117 // Avoid negative numbers
118 if (point.x == 0 || point.y == 0) {
119 continue;
120 }
121
122 std::array<Common::Point<std::size_t>, 4> new_points{
123 Common::Point<std::size_t>{point.x - 1, point.y},
124 {point.x, point.y - 1},
125 {point.x + 1, point.y},
126 {point.x, point.y + 1},
127 };
128
129 for (const auto new_point : new_points) {
130 if (new_point.x < 0 || new_point.x >= width) {
131 continue;
132 }
133 if (new_point.y < 0 || new_point.y >= height) {
134 continue;
135 }
136 if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
137 continue;
138 }
139 const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
140 current_cluster = MergeCluster(current_cluster, cluster);
141 SetPixel(data, new_point.x, new_point.y, 0);
142 search_points.push({new_point.x, new_point.y});
143 }
144 }
145
146 return current_cluster;
147}
148
149ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
150 const std::vector<u8>& data, std::size_t x, std::size_t y) const {
151 return {
152 .average_intensity = GetPixel(data, x, y) / 255.0f,
153 .centroid =
154 {
155 .x = static_cast<f32>(x),
156 .y = static_cast<f32>(y),
157
158 },
159 .pixel_count = 1,
160 .bound =
161 {
162 .x = static_cast<s16>(x),
163 .y = static_cast<s16>(y),
164 .width = 1,
165 .height = 1,
166 },
167 };
168}
169
170ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
171 const ClusteringData a, const ClusteringData b) const {
172 const u32 pixel_count = a.pixel_count + b.pixel_count;
173 const f32 average_intensitiy =
174 (a.average_intensity * a.pixel_count + b.average_intensity * b.pixel_count) / pixel_count;
175 const Core::IrSensor::IrsCentroid centroid = {
176 .x = (a.centroid.x * a.pixel_count + b.centroid.x * b.pixel_count) / pixel_count,
177 .y = (a.centroid.y * a.pixel_count + b.centroid.y * b.pixel_count) / pixel_count,
178 };
179 s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
180 s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
181 s16 a_bound_end_x = a.bound.x + a.bound.width;
182 s16 a_bound_end_y = a.bound.y + a.bound.height;
183 s16 b_bound_end_x = b.bound.x + b.bound.width;
184 s16 b_bound_end_y = b.bound.y + b.bound.height;
185
186 const Core::IrSensor::IrsRect bound = {
187 .x = bound_start_x,
188 .y = bound_start_y,
189 .width = a_bound_end_x > b_bound_end_x ? a_bound_end_x - bound_start_x
190 : b_bound_end_x - bound_start_x,
191 .height = a_bound_end_y > b_bound_end_y ? a_bound_end_y - bound_start_y
192 : b_bound_end_y - bound_start_y,
193 };
194
195 return {
196 .average_intensity = average_intensitiy,
197 .centroid = centroid,
198 .pixel_count = pixel_count,
199 .bound = bound,
200 };
201}
202
203u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
204 if ((y * width) + x > data.size()) {
205 return 0;
206 }
207 return data[(y * width) + x];
208}
209
210void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
211 if ((y * width) + x > data.size()) {
212 return;
213 }
214 data[(y * width) + x] = value;
215}
216
217void ClusteringProcessor::SetDefaultConfig() {
218 current_config.camera_config.exposure_time = 200000;
219 current_config.camera_config.gain = 2;
220 current_config.camera_config.is_negative_used = false;
221 current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
222 current_config.window_of_interest = {
223 .x = 0,
224 .y = 0,
225 .width = width,
226 .height = height,
227 };
228 current_config.pixel_count_min = 3;
229 current_config.pixel_count_max = 0x12C00;
230 current_config.is_external_light_filter_enabled = true;
231 current_config.object_intensity_min = 150;
232
233 npad_device->SetCameraFormat(format);
234}
235
22void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { 236void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
23 current_config.camera_config.exposure_time = config.camera_config.exposure_time; 237 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
24 current_config.camera_config.gain = config.camera_config.gain; 238 current_config.camera_config.gain = config.camera_config.gain;
25 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; 239 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
26 current_config.camera_config.light_target = 240 current_config.camera_config.light_target =
27 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); 241 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
242 current_config.window_of_interest = config.window_of_interest;
28 current_config.pixel_count_min = config.pixel_count_min; 243 current_config.pixel_count_min = config.pixel_count_min;
29 current_config.pixel_count_max = config.pixel_count_max; 244 current_config.pixel_count_max = config.pixel_count_max;
30 current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; 245 current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
31 current_config.object_intensity_min = config.object_intensity_min; 246 current_config.object_intensity_min = config.object_intensity_min;
247
248 LOG_INFO(Service_IRS,
249 "Processor config, exposure_time={}, gain={}, is_negative_used={}, "
250 "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
251 "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
252 current_config.camera_config.exposure_time, current_config.camera_config.gain,
253 current_config.camera_config.is_negative_used,
254 current_config.camera_config.light_target, current_config.window_of_interest.x,
255 current_config.window_of_interest.y, current_config.window_of_interest.width,
256 current_config.window_of_interest.height, current_config.pixel_count_min,
257 current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
258 current_config.object_intensity_min);
259
260 npad_device->SetCameraFormat(format);
32} 261}
33 262
34} // namespace Service::IRS 263} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
index 6e2ba8846..dc01a8ea7 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.h
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -5,12 +5,19 @@
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hid/irs_types.h" 7#include "core/hid/irs_types.h"
8#include "core/hle/service/hid/irs_ring_lifo.h"
8#include "core/hle/service/hid/irsensor/processor_base.h" 9#include "core/hle/service/hid/irsensor/processor_base.h"
9 10
11namespace Core::HID {
12class EmulatedController;
13} // namespace Core::HID
14
10namespace Service::IRS { 15namespace Service::IRS {
11class ClusteringProcessor final : public ProcessorBase { 16class ClusteringProcessor final : public ProcessorBase {
12public: 17public:
13 explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format); 18 explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
19 Core::IrSensor::DeviceFormat& device_format,
20 std::size_t npad_index);
14 ~ClusteringProcessor() override; 21 ~ClusteringProcessor() override;
15 22
16 // Called when the processor is initialized 23 // Called when the processor is initialized
@@ -26,6 +33,10 @@ public:
26 void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config); 33 void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
27 34
28private: 35private:
36 static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
37 static constexpr std::size_t width = 320;
38 static constexpr std::size_t height = 240;
39
29 // This is nn::irsensor::ClusteringProcessorConfig 40 // This is nn::irsensor::ClusteringProcessorConfig
30 struct ClusteringProcessorConfig { 41 struct ClusteringProcessorConfig {
31 Core::IrSensor::CameraConfig camera_config; 42 Core::IrSensor::CameraConfig camera_config;
@@ -68,7 +79,32 @@ private:
68 static_assert(sizeof(ClusteringProcessorState) == 0x198, 79 static_assert(sizeof(ClusteringProcessorState) == 0x198,
69 "ClusteringProcessorState is an invalid size"); 80 "ClusteringProcessorState is an invalid size");
70 81
82 struct ClusteringSharedMemory {
83 Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
84 static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
85 INSERT_PADDING_WORDS(0x11F);
86 };
87 static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
88 "ClusteringSharedMemory is an invalid size");
89
90 void OnControllerUpdate(Core::HID::ControllerTriggerType type);
91 void RemoveLowIntensityData(std::vector<u8>& data);
92 ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
93 ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
94 std::size_t y) const;
95 ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
96 u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
97 void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
98
99 // Sets config parameters of the camera
100 void SetDefaultConfig();
101
102 ClusteringSharedMemory* shared_memory = nullptr;
103 ClusteringProcessorState next_state{};
104
71 ClusteringProcessorConfig current_config{}; 105 ClusteringProcessorConfig current_config{};
72 Core::IrSensor::DeviceFormat& device; 106 Core::IrSensor::DeviceFormat& device;
107 Core::HID::EmulatedController* npad_device;
108 int callback_key{};
73}; 109};
74} // namespace Service::IRS 110} // namespace Service::IRS
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 0ee3820a2..3acb61f03 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -838,7 +838,7 @@ void GRenderWindow::InitializeCamera() {
838 camera_timer = std::make_unique<QTimer>(); 838 camera_timer = std::make_unique<QTimer>();
839 connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); }); 839 connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
840 // This timer should be dependent of camera resolution 5ms for every 100 pixels 840 // This timer should be dependent of camera resolution 5ms for every 100 pixels
841 camera_timer->start(100); 841 camera_timer->start(50);
842} 842}
843 843
844void GRenderWindow::FinalizeCamera() { 844void GRenderWindow::FinalizeCamera() {