diff options
| author | 2024-01-04 20:37:43 -0600 | |
|---|---|---|
| committer | 2024-01-05 11:41:15 -0600 | |
| commit | ee847f8ff0b1b0aec39c1b78c010bc0c08a0a613 (patch) | |
| tree | 3b95cbb74be05f0ce7a007353f1f9f95e1ed3901 /src/hid_core/irsensor | |
| parent | Merge pull request #12437 from ameerj/gl-amd-fixes (diff) | |
| download | yuzu-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.cpp | 267 | ||||
| -rw-r--r-- | src/hid_core/irsensor/clustering_processor.h | 115 | ||||
| -rw-r--r-- | src/hid_core/irsensor/image_transfer_processor.cpp | 155 | ||||
| -rw-r--r-- | src/hid_core/irsensor/image_transfer_processor.h | 77 | ||||
| -rw-r--r-- | src/hid_core/irsensor/ir_led_processor.cpp | 27 | ||||
| -rw-r--r-- | src/hid_core/irsensor/ir_led_processor.h | 47 | ||||
| -rw-r--r-- | src/hid_core/irsensor/irs_types.h | 301 | ||||
| -rw-r--r-- | src/hid_core/irsensor/moment_processor.cpp | 149 | ||||
| -rw-r--r-- | src/hid_core/irsensor/moment_processor.h | 91 | ||||
| -rw-r--r-- | src/hid_core/irsensor/pointing_processor.cpp | 26 | ||||
| -rw-r--r-- | src/hid_core/irsensor/pointing_processor.h | 61 | ||||
| -rw-r--r-- | src/hid_core/irsensor/processor_base.cpp | 67 | ||||
| -rw-r--r-- | src/hid_core/irsensor/processor_base.h | 33 | ||||
| -rw-r--r-- | src/hid_core/irsensor/tera_plugin_processor.cpp | 29 | ||||
| -rw-r--r-- | src/hid_core/irsensor/tera_plugin_processor.h | 53 |
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 | |||
| 12 | namespace Service::IRS { | ||
| 13 | ClusteringProcessor::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 | |||
| 34 | ClusteringProcessor::~ClusteringProcessor() { | ||
| 35 | npad_device->DeleteCallback(callback_key); | ||
| 36 | }; | ||
| 37 | |||
| 38 | void ClusteringProcessor::StartProcessor() { | ||
| 39 | device.camera_status = Core::IrSensor::IrCameraStatus::Available; | ||
| 40 | device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; | ||
| 41 | } | ||
| 42 | |||
| 43 | void ClusteringProcessor::SuspendProcessor() {} | ||
| 44 | |||
| 45 | void ClusteringProcessor::StopProcessor() {} | ||
| 46 | |||
| 47 | void 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 | |||
| 97 | void 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 | |||
| 105 | ClusteringProcessor::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 | |||
| 150 | ClusteringProcessor::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 | |||
| 171 | ClusteringProcessor::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 | |||
| 206 | u8 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 | |||
| 213 | void 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 | |||
| 220 | void 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 | |||
| 240 | void 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 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Core::HID { | ||
| 16 | class EmulatedController; | ||
| 17 | } // namespace Core::HID | ||
| 18 | |||
| 19 | namespace Service::IRS { | ||
| 20 | class ClusteringProcessor final : public ProcessorBase { | ||
| 21 | public: | ||
| 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 | |||
| 38 | private: | ||
| 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 | |||
| 10 | namespace Service::IRS { | ||
| 11 | ImageTransferProcessor::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 | |||
| 28 | ImageTransferProcessor::~ImageTransferProcessor() { | ||
| 29 | npad_device->DeleteCallback(callback_key); | ||
| 30 | }; | ||
| 31 | |||
| 32 | void 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 | |||
| 40 | void ImageTransferProcessor::SuspendProcessor() {} | ||
| 41 | |||
| 42 | void ImageTransferProcessor::StopProcessor() {} | ||
| 43 | |||
| 44 | void 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 | |||
| 110 | void 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 | |||
| 126 | void 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 | |||
| 143 | void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { | ||
| 144 | transfer_memory = t_mem; | ||
| 145 | } | ||
| 146 | |||
| 147 | Core::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 | |||
| 10 | namespace Core { | ||
| 11 | class System; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Core::HID { | ||
| 15 | class EmulatedController; | ||
| 16 | } // namespace Core::HID | ||
| 17 | |||
| 18 | namespace Service::IRS { | ||
| 19 | class ImageTransferProcessor final : public ProcessorBase { | ||
| 20 | public: | ||
| 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 | |||
| 44 | private: | ||
| 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 | |||
| 6 | namespace Service::IRS { | ||
| 7 | IrLedProcessor::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 | |||
| 14 | IrLedProcessor::~IrLedProcessor() = default; | ||
| 15 | |||
| 16 | void IrLedProcessor::StartProcessor() {} | ||
| 17 | |||
| 18 | void IrLedProcessor::SuspendProcessor() {} | ||
| 19 | |||
| 20 | void IrLedProcessor::StopProcessor() {} | ||
| 21 | |||
| 22 | void 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 | |||
| 11 | namespace Service::IRS { | ||
| 12 | class IrLedProcessor final : public ProcessorBase { | ||
| 13 | public: | ||
| 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 | |||
| 29 | private: | ||
| 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 | |||
| 10 | namespace Core::IrSensor { | ||
| 11 | |||
| 12 | // This is nn::irsensor::CameraAmbientNoiseLevel | ||
| 13 | enum 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 | ||
| 21 | enum class CameraLightTarget : u32 { | ||
| 22 | AllLeds, | ||
| 23 | BrightLeds, | ||
| 24 | DimLeds, | ||
| 25 | None, | ||
| 26 | }; | ||
| 27 | |||
| 28 | // This is nn::irsensor::PackedCameraLightTarget | ||
| 29 | enum class PackedCameraLightTarget : u8 { | ||
| 30 | AllLeds, | ||
| 31 | BrightLeds, | ||
| 32 | DimLeds, | ||
| 33 | None, | ||
| 34 | }; | ||
| 35 | |||
| 36 | // This is nn::irsensor::AdaptiveClusteringMode | ||
| 37 | enum class AdaptiveClusteringMode : u32 { | ||
| 38 | StaticFov, | ||
| 39 | DynamicFov, | ||
| 40 | }; | ||
| 41 | |||
| 42 | // This is nn::irsensor::AdaptiveClusteringTargetDistance | ||
| 43 | enum class AdaptiveClusteringTargetDistance : u32 { | ||
| 44 | Near, | ||
| 45 | Middle, | ||
| 46 | Far, | ||
| 47 | }; | ||
| 48 | |||
| 49 | // This is nn::irsensor::ImageTransferProcessorFormat | ||
| 50 | enum class ImageTransferProcessorFormat : u32 { | ||
| 51 | Size320x240, | ||
| 52 | Size160x120, | ||
| 53 | Size80x60, | ||
| 54 | Size40x30, | ||
| 55 | Size20x15, | ||
| 56 | }; | ||
| 57 | |||
| 58 | // This is nn::irsensor::PackedImageTransferProcessorFormat | ||
| 59 | enum class PackedImageTransferProcessorFormat : u8 { | ||
| 60 | Size320x240, | ||
| 61 | Size160x120, | ||
| 62 | Size80x60, | ||
| 63 | Size40x30, | ||
| 64 | Size20x15, | ||
| 65 | }; | ||
| 66 | |||
| 67 | // This is nn::irsensor::IrCameraStatus | ||
| 68 | enum class IrCameraStatus : u32 { | ||
| 69 | Available, | ||
| 70 | Unsupported, | ||
| 71 | Unconnected, | ||
| 72 | }; | ||
| 73 | |||
| 74 | // This is nn::irsensor::IrCameraInternalStatus | ||
| 75 | enum 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 | ||
| 88 | enum 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 | ||
| 99 | enum ImageProcessorStatus : u32 { | ||
| 100 | Stopped, | ||
| 101 | Running, | ||
| 102 | }; | ||
| 103 | |||
| 104 | // This is nn::irsensor::HandAnalysisMode | ||
| 105 | enum class HandAnalysisMode : u32 { | ||
| 106 | None, | ||
| 107 | Silhouette, | ||
| 108 | Image, | ||
| 109 | SilhoueteAndImage, | ||
| 110 | SilhuetteOnly, | ||
| 111 | }; | ||
| 112 | |||
| 113 | // This is nn::irsensor::IrSensorFunctionLevel | ||
| 114 | enum class IrSensorFunctionLevel : u8 { | ||
| 115 | unknown0, | ||
| 116 | unknown1, | ||
| 117 | unknown2, | ||
| 118 | unknown3, | ||
| 119 | unknown4, | ||
| 120 | }; | ||
| 121 | |||
| 122 | // This is nn::irsensor::MomentProcessorPreprocess | ||
| 123 | enum class MomentProcessorPreprocess : u32 { | ||
| 124 | Unknown0, | ||
| 125 | Unknown1, | ||
| 126 | }; | ||
| 127 | |||
| 128 | // This is nn::irsensor::PackedMomentProcessorPreprocess | ||
| 129 | enum class PackedMomentProcessorPreprocess : u8 { | ||
| 130 | Unknown0, | ||
| 131 | Unknown1, | ||
| 132 | }; | ||
| 133 | |||
| 134 | // This is nn::irsensor::PointingStatus | ||
| 135 | enum class PointingStatus : u32 { | ||
| 136 | Unknown0, | ||
| 137 | Unknown1, | ||
| 138 | }; | ||
| 139 | |||
| 140 | struct IrsRect { | ||
| 141 | s16 x; | ||
| 142 | s16 y; | ||
| 143 | s16 width; | ||
| 144 | s16 height; | ||
| 145 | }; | ||
| 146 | |||
| 147 | struct IrsCentroid { | ||
| 148 | f32 x; | ||
| 149 | f32 y; | ||
| 150 | }; | ||
| 151 | |||
| 152 | struct CameraConfig { | ||
| 153 | u64 exposure_time; | ||
| 154 | CameraLightTarget light_target; | ||
| 155 | u32 gain; | ||
| 156 | bool is_negative_used; | ||
| 157 | INSERT_PADDING_BYTES(7); | ||
| 158 | }; | ||
| 159 | static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size"); | ||
| 160 | |||
| 161 | struct PackedCameraConfig { | ||
| 162 | u64 exposure_time; | ||
| 163 | PackedCameraLightTarget light_target; | ||
| 164 | u8 gain; | ||
| 165 | bool is_negative_used; | ||
| 166 | INSERT_PADDING_BYTES(5); | ||
| 167 | }; | ||
| 168 | static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size"); | ||
| 169 | |||
| 170 | // This is nn::irsensor::IrCameraHandle | ||
| 171 | struct IrCameraHandle { | ||
| 172 | u8 npad_id{}; | ||
| 173 | Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; | ||
| 174 | INSERT_PADDING_BYTES(2); | ||
| 175 | }; | ||
| 176 | static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); | ||
| 177 | |||
| 178 | // This is nn::irsensor::PackedMcuVersion | ||
| 179 | struct PackedMcuVersion { | ||
| 180 | u16 major; | ||
| 181 | u16 minor; | ||
| 182 | }; | ||
| 183 | static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); | ||
| 184 | |||
| 185 | // This is nn::irsensor::PackedMomentProcessorConfig | ||
| 186 | struct 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 | }; | ||
| 194 | static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, | ||
| 195 | "PackedMomentProcessorConfig is an invalid size"); | ||
| 196 | |||
| 197 | // This is nn::irsensor::PackedClusteringProcessorConfig | ||
| 198 | struct 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 | }; | ||
| 208 | static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28, | ||
| 209 | "PackedClusteringProcessorConfig is an invalid size"); | ||
| 210 | |||
| 211 | // This is nn::irsensor::PackedImageTransferProcessorConfig | ||
| 212 | struct PackedImageTransferProcessorConfig { | ||
| 213 | PackedCameraConfig camera_config; | ||
| 214 | PackedMcuVersion required_mcu_version; | ||
| 215 | PackedImageTransferProcessorFormat format; | ||
| 216 | INSERT_PADDING_BYTES(3); | ||
| 217 | }; | ||
| 218 | static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, | ||
| 219 | "PackedImageTransferProcessorConfig is an invalid size"); | ||
| 220 | |||
| 221 | // This is nn::irsensor::PackedTeraPluginProcessorConfig | ||
| 222 | struct PackedTeraPluginProcessorConfig { | ||
| 223 | PackedMcuVersion required_mcu_version; | ||
| 224 | u8 mode; | ||
| 225 | u8 unknown_1; | ||
| 226 | u8 unknown_2; | ||
| 227 | u8 unknown_3; | ||
| 228 | }; | ||
| 229 | static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, | ||
| 230 | "PackedTeraPluginProcessorConfig is an invalid size"); | ||
| 231 | |||
| 232 | // This is nn::irsensor::PackedPointingProcessorConfig | ||
| 233 | struct PackedPointingProcessorConfig { | ||
| 234 | IrsRect window_of_interest; | ||
| 235 | PackedMcuVersion required_mcu_version; | ||
| 236 | }; | ||
| 237 | static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, | ||
| 238 | "PackedPointingProcessorConfig is an invalid size"); | ||
| 239 | |||
| 240 | // This is nn::irsensor::PackedFunctionLevel | ||
| 241 | struct PackedFunctionLevel { | ||
| 242 | IrSensorFunctionLevel function_level; | ||
| 243 | INSERT_PADDING_BYTES(3); | ||
| 244 | }; | ||
| 245 | static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); | ||
| 246 | |||
| 247 | // This is nn::irsensor::PackedImageTransferProcessorExConfig | ||
| 248 | struct 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 | }; | ||
| 258 | static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, | ||
| 259 | "PackedImageTransferProcessorExConfig is an invalid size"); | ||
| 260 | |||
| 261 | // This is nn::irsensor::PackedIrLedProcessorConfig | ||
| 262 | struct PackedIrLedProcessorConfig { | ||
| 263 | PackedMcuVersion required_mcu_version; | ||
| 264 | u8 light_target; | ||
| 265 | INSERT_PADDING_BYTES(3); | ||
| 266 | }; | ||
| 267 | static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, | ||
| 268 | "PackedIrLedProcessorConfig is an invalid size"); | ||
| 269 | |||
| 270 | // This is nn::irsensor::HandAnalysisConfig | ||
| 271 | struct HandAnalysisConfig { | ||
| 272 | HandAnalysisMode mode; | ||
| 273 | }; | ||
| 274 | static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size"); | ||
| 275 | |||
| 276 | // This is nn::irsensor::detail::ProcessorState contents are different for each processor | ||
| 277 | struct ProcessorState { | ||
| 278 | std::array<u8, 0xE20> processor_raw_data{}; | ||
| 279 | }; | ||
| 280 | static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size"); | ||
| 281 | |||
| 282 | // This is nn::irsensor::detail::DeviceFormat | ||
| 283 | struct 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 | }; | ||
| 290 | static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size"); | ||
| 291 | |||
| 292 | // This is nn::irsensor::ImageTransferProcessorState | ||
| 293 | struct ImageTransferProcessorState { | ||
| 294 | u64 sampling_number; | ||
| 295 | Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; | ||
| 296 | INSERT_PADDING_BYTES(4); | ||
| 297 | }; | ||
| 298 | static_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 | |||
| 10 | namespace Service::IRS { | ||
| 11 | static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30; | ||
| 12 | static constexpr std::size_t ImageWidth = 40; | ||
| 13 | static constexpr std::size_t ImageHeight = 30; | ||
| 14 | |||
| 15 | MomentProcessor::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 | |||
| 34 | MomentProcessor::~MomentProcessor() { | ||
| 35 | npad_device->DeleteCallback(callback_key); | ||
| 36 | }; | ||
| 37 | |||
| 38 | void MomentProcessor::StartProcessor() { | ||
| 39 | device.camera_status = Core::IrSensor::IrCameraStatus::Available; | ||
| 40 | device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; | ||
| 41 | } | ||
| 42 | |||
| 43 | void MomentProcessor::SuspendProcessor() {} | ||
| 44 | |||
| 45 | void MomentProcessor::StopProcessor() {} | ||
| 46 | |||
| 47 | void 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 | |||
| 82 | u8 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 | |||
| 89 | MomentProcessor::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 | |||
| 135 | void 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 | |||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Core::HID { | ||
| 17 | class EmulatedController; | ||
| 18 | } // namespace Core::HID | ||
| 19 | |||
| 20 | namespace Service::IRS { | ||
| 21 | class MomentProcessor final : public ProcessorBase { | ||
| 22 | public: | ||
| 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 | |||
| 39 | private: | ||
| 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 | |||
| 6 | namespace Service::IRS { | ||
| 7 | PointingProcessor::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 | |||
| 14 | PointingProcessor::~PointingProcessor() = default; | ||
| 15 | |||
| 16 | void PointingProcessor::StartProcessor() {} | ||
| 17 | |||
| 18 | void PointingProcessor::SuspendProcessor() {} | ||
| 19 | |||
| 20 | void PointingProcessor::StopProcessor() {} | ||
| 21 | |||
| 22 | void 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 | |||
| 10 | namespace Service::IRS { | ||
| 11 | class PointingProcessor final : public ProcessorBase { | ||
| 12 | public: | ||
| 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 | |||
| 28 | private: | ||
| 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 | |||
| 6 | namespace Service::IRS { | ||
| 7 | |||
| 8 | ProcessorBase::ProcessorBase() {} | ||
| 9 | ProcessorBase::~ProcessorBase() = default; | ||
| 10 | |||
| 11 | bool ProcessorBase::IsProcessorActive() const { | ||
| 12 | return is_active; | ||
| 13 | } | ||
| 14 | |||
| 15 | std::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 | |||
| 32 | std::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 | |||
| 49 | std::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 | |||
| 9 | namespace Service::IRS { | ||
| 10 | class ProcessorBase { | ||
| 11 | public: | ||
| 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 | |||
| 21 | protected: | ||
| 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 | |||
| 6 | namespace Service::IRS { | ||
| 7 | TeraPluginProcessor::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 | |||
| 14 | TeraPluginProcessor::~TeraPluginProcessor() = default; | ||
| 15 | |||
| 16 | void TeraPluginProcessor::StartProcessor() {} | ||
| 17 | |||
| 18 | void TeraPluginProcessor::SuspendProcessor() {} | ||
| 19 | |||
| 20 | void TeraPluginProcessor::StopProcessor() {} | ||
| 21 | |||
| 22 | void 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 | |||
| 11 | namespace Service::IRS { | ||
| 12 | class TeraPluginProcessor final : public ProcessorBase { | ||
| 13 | public: | ||
| 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 | |||
| 29 | private: | ||
| 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 | ||