summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar liamwhite2022-07-27 15:54:28 -0400
committerGravatar GitHub2022-07-27 15:54:28 -0400
commit64fd9f41a7d1170dbfa827f796033c4f2cad0cf0 (patch)
tree4ccf48f3361f4bd2e4c35b5c9a48e42c12892564 /src
parentMerge pull request #8633 from Morph1984/optional-keys (diff)
parentAddress comments (diff)
downloadyuzu-64fd9f41a7d1170dbfa827f796033c4f2cad0cf0.tar.gz
yuzu-64fd9f41a7d1170dbfa827f796033c4f2cad0cf0.tar.xz
yuzu-64fd9f41a7d1170dbfa827f796033c4f2cad0cf0.zip
Merge pull request #8636 from german77/irs_cluster_release
service: irs: Implement clustering processor
Diffstat (limited to 'src')
-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.cpp239
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h38
-rw-r--r--src/yuzu/bootmanager.cpp3
6 files changed, 323 insertions, 7 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a614b176b..40b1ea4a2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -451,6 +451,7 @@ add_library(core STATIC
451 hle/service/hid/hidbus.h 451 hle/service/hid/hidbus.h
452 hle/service/hid/irs.cpp 452 hle/service/hid/irs.cpp
453 hle/service/hid/irs.h 453 hle/service/hid/irs.h
454 hle/service/hid/irs_ring_lifo.h
454 hle/service/hid/ring_lifo.h 455 hle/service/hid/ring_lifo.h
455 hle/service/hid/xcd.cpp 456 hle/service/hid/xcd.cpp
456 hle/service/hid/xcd.h 457 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..255d1d296
--- /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 < static_cast<s64>(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..e2f4ae876 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -1,34 +1,265 @@
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 auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
57 const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
58 const auto window_end_x =
59 window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
60 const auto window_end_y =
61 window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
62
63 for (std::size_t y = window_start_y; y < window_end_y; y++) {
64 for (std::size_t x = window_start_x; x < window_end_x; x++) {
65 u8 pixel = GetPixel(filtered_image, x, y);
66 if (pixel == 0) {
67 continue;
68 }
69 const auto cluster = GetClusterProperties(filtered_image, x, y);
70 if (cluster.pixel_count > current_config.pixel_count_max) {
71 continue;
72 }
73 if (cluster.pixel_count < current_config.pixel_count_min) {
74 continue;
75 }
76 // Cluster object limit reached
77 if (next_state.object_count >= next_state.data.size()) {
78 continue;
79 }
80 next_state.data[next_state.object_count] = cluster;
81 next_state.object_count++;
82 }
83 }
84
85 next_state.sampling_number = camera_data.sample;
86 next_state.timestamp = next_state.timestamp + 131;
87 next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
88 shared_memory->clustering_lifo.WriteNextEntry(next_state);
89
90 if (!IsProcessorActive()) {
91 StartProcessor();
92 }
93}
94
95void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
96 for (u8& pixel : data) {
97 if (pixel < current_config.pixel_count_min) {
98 pixel = 0;
99 }
100 }
101}
102
103ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
104 std::size_t x,
105 std::size_t y) {
106 using DataPoint = Common::Point<std::size_t>;
107 std::queue<DataPoint> search_points{};
108 ClusteringData current_cluster = GetPixelProperties(data, x, y);
109 SetPixel(data, x, y, 0);
110 search_points.emplace<DataPoint>({x, y});
111
112 while (!search_points.empty()) {
113 const auto point = search_points.front();
114 search_points.pop();
115
116 // Avoid negative numbers
117 if (point.x == 0 || point.y == 0) {
118 continue;
119 }
120
121 std::array<DataPoint, 4> new_points{
122 DataPoint{point.x - 1, point.y},
123 {point.x, point.y - 1},
124 {point.x + 1, point.y},
125 {point.x, point.y + 1},
126 };
127
128 for (const auto new_point : new_points) {
129 if (new_point.x >= width) {
130 continue;
131 }
132 if (new_point.y >= height) {
133 continue;
134 }
135 if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
136 continue;
137 }
138 const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
139 current_cluster = MergeCluster(current_cluster, cluster);
140 SetPixel(data, new_point.x, new_point.y, 0);
141 search_points.emplace<DataPoint>({new_point.x, new_point.y});
142 }
143 }
144
145 return current_cluster;
146}
147
148ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
149 const std::vector<u8>& data, std::size_t x, std::size_t y) const {
150 return {
151 .average_intensity = GetPixel(data, x, y) / 255.0f,
152 .centroid =
153 {
154 .x = static_cast<f32>(x),
155 .y = static_cast<f32>(y),
156
157 },
158 .pixel_count = 1,
159 .bound =
160 {
161 .x = static_cast<s16>(x),
162 .y = static_cast<s16>(y),
163 .width = 1,
164 .height = 1,
165 },
166 };
167}
168
169ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
170 const ClusteringData a, const ClusteringData b) const {
171 const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
172 const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
173 const f32 pixel_count = a_pixel_count + b_pixel_count;
174 const f32 average_intensity =
175 (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
176 const Core::IrSensor::IrsCentroid centroid = {
177 .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
178 .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
179 };
180 s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
181 s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
182 s16 a_bound_end_x = a.bound.x + a.bound.width;
183 s16 a_bound_end_y = a.bound.y + a.bound.height;
184 s16 b_bound_end_x = b.bound.x + b.bound.width;
185 s16 b_bound_end_y = b.bound.y + b.bound.height;
186
187 const Core::IrSensor::IrsRect bound = {
188 .x = bound_start_x,
189 .y = bound_start_y,
190 .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
191 : static_cast<s16>(b_bound_end_x - bound_start_x),
192 .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
193 : static_cast<s16>(b_bound_end_y - bound_start_y),
194 };
195
196 return {
197 .average_intensity = average_intensity,
198 .centroid = centroid,
199 .pixel_count = static_cast<u32>(pixel_count),
200 .bound = bound,
201 };
202}
203
204u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
205 if ((y * width) + x > data.size()) {
206 return 0;
207 }
208 return data[(y * width) + x];
209}
210
211void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
212 if ((y * width) + x > data.size()) {
213 return;
214 }
215 data[(y * width) + x] = value;
216}
217
218void ClusteringProcessor::SetDefaultConfig() {
219 using namespace std::literals::chrono_literals;
220 current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
221 current_config.camera_config.gain = 2;
222 current_config.camera_config.is_negative_used = false;
223 current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
224 current_config.window_of_interest = {
225 .x = 0,
226 .y = 0,
227 .width = width,
228 .height = height,
229 };
230 current_config.pixel_count_min = 3;
231 current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
232 current_config.is_external_light_filter_enabled = true;
233 current_config.object_intensity_min = 150;
234
235 npad_device->SetCameraFormat(format);
236}
237
22void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { 238void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
23 current_config.camera_config.exposure_time = config.camera_config.exposure_time; 239 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
24 current_config.camera_config.gain = config.camera_config.gain; 240 current_config.camera_config.gain = config.camera_config.gain;
25 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; 241 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
26 current_config.camera_config.light_target = 242 current_config.camera_config.light_target =
27 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); 243 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
244 current_config.window_of_interest = config.window_of_interest;
28 current_config.pixel_count_min = config.pixel_count_min; 245 current_config.pixel_count_min = config.pixel_count_min;
29 current_config.pixel_count_max = config.pixel_count_max; 246 current_config.pixel_count_max = config.pixel_count_max;
30 current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; 247 current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
31 current_config.object_intensity_min = config.object_intensity_min; 248 current_config.object_intensity_min = config.object_intensity_min;
249
250 LOG_INFO(Service_IRS,
251 "Processor config, exposure_time={}, gain={}, is_negative_used={}, "
252 "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
253 "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
254 current_config.camera_config.exposure_time, current_config.camera_config.gain,
255 current_config.camera_config.is_negative_used,
256 current_config.camera_config.light_target, current_config.window_of_interest.x,
257 current_config.window_of_interest.y, current_config.window_of_interest.width,
258 current_config.window_of_interest.height, current_config.pixel_count_min,
259 current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
260 current_config.object_intensity_min);
261
262 npad_device->SetCameraFormat(format);
32} 263}
33 264
34} // namespace Service::IRS 265} // 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 6a7f48b6f..ef3bdfb1a 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -804,6 +804,7 @@ void GRenderWindow::TouchEndEvent() {
804} 804}
805 805
806void GRenderWindow::InitializeCamera() { 806void GRenderWindow::InitializeCamera() {
807 constexpr auto camera_update_ms = std::chrono::milliseconds{50}; // (50ms, 20Hz)
807 if (!Settings::values.enable_ir_sensor) { 808 if (!Settings::values.enable_ir_sensor) {
808 return; 809 return;
809 } 810 }
@@ -837,7 +838,7 @@ void GRenderWindow::InitializeCamera() {
837 camera_timer = std::make_unique<QTimer>(); 838 camera_timer = std::make_unique<QTimer>();
838 connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); }); 839 connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
839 // 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
840 camera_timer->start(100); 841 camera_timer->start(camera_update_ms);
841} 842}
842 843
843void GRenderWindow::FinalizeCamera() { 844void GRenderWindow::FinalizeCamera() {