Browse Source
Merge pull request #8484 from german77/irs_release
Merge pull request #8484 from german77/irs_release
service: irs: Add camera support, split processors and implement ImageTransferProcessornce_cpp
committed by
GitHub
50 changed files with 2342 additions and 320 deletions
-
6CMakeLists.txt
-
8CMakeModules/CopyYuzuQt5Deps.cmake
-
29src/common/input.h
-
3src/common/settings.h
-
15src/core/CMakeLists.txt
-
59src/core/hid/emulated_controller.cpp
-
40src/core/hid/emulated_controller.h
-
14src/core/hid/input_converter.cpp
-
8src/core/hid/input_converter.h
-
301src/core/hid/irs_types.h
-
7src/core/hle/service/hid/errors.h
-
4src/core/hle/service/hid/hid.cpp
-
324src/core/hle/service/hid/irs.cpp
-
295src/core/hle/service/hid/irs.h
-
34src/core/hle/service/hid/irsensor/clustering_processor.cpp
-
74src/core/hle/service/hid/irsensor/clustering_processor.h
-
150src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
-
73src/core/hle/service/hid/irsensor/image_transfer_processor.h
-
27src/core/hle/service/hid/irsensor/ir_led_processor.cpp
-
47src/core/hle/service/hid/irsensor/ir_led_processor.h
-
34src/core/hle/service/hid/irsensor/moment_processor.cpp
-
61src/core/hle/service/hid/irsensor/moment_processor.h
-
26src/core/hle/service/hid/irsensor/pointing_processor.cpp
-
61src/core/hle/service/hid/irsensor/pointing_processor.h
-
67src/core/hle/service/hid/irsensor/processor_base.cpp
-
33src/core/hle/service/hid/irsensor/processor_base.h
-
29src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
-
53src/core/hle/service/hid/irsensor/tera_plugin_processor.h
-
2src/input_common/CMakeLists.txt
-
82src/input_common/drivers/camera.cpp
-
29src/input_common/drivers/camera.h
-
38src/input_common/input_engine.cpp
-
19src/input_common/input_engine.h
-
60src/input_common/input_poller.cpp
-
11src/input_common/input_poller.h
-
21src/input_common/main.cpp
-
9src/input_common/main.h
-
5src/yuzu/CMakeLists.txt
-
82src/yuzu/bootmanager.cpp
-
14src/yuzu/bootmanager.h
-
12src/yuzu/configuration/config.cpp
-
2src/yuzu/configuration/config.h
-
138src/yuzu/configuration/configure_camera.cpp
-
54src/yuzu/configuration/configure_camera.h
-
170src/yuzu/configuration/configure_camera.ui
-
5src/yuzu/configuration/configure_input.cpp
-
3src/yuzu/configuration/configure_input_advanced.cpp
-
1src/yuzu/configuration/configure_input_advanced.h
-
14src/yuzu/configuration/configure_input_advanced.ui
-
9src/yuzu/main.cpp
@ -0,0 +1,301 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_funcs.h" |
|||
#include "common/common_types.h" |
|||
#include "core/hid/hid_types.h" |
|||
|
|||
namespace Core::IrSensor { |
|||
|
|||
// This is nn::irsensor::CameraAmbientNoiseLevel |
|||
enum class CameraAmbientNoiseLevel : u32 { |
|||
Low, |
|||
Medium, |
|||
High, |
|||
Unkown3, // This level can't be reached |
|||
}; |
|||
|
|||
// This is nn::irsensor::CameraLightTarget |
|||
enum class CameraLightTarget : u32 { |
|||
AllLeds, |
|||
BrightLeds, |
|||
DimLeds, |
|||
None, |
|||
}; |
|||
|
|||
// This is nn::irsensor::PackedCameraLightTarget |
|||
enum class PackedCameraLightTarget : u8 { |
|||
AllLeds, |
|||
BrightLeds, |
|||
DimLeds, |
|||
None, |
|||
}; |
|||
|
|||
// This is nn::irsensor::AdaptiveClusteringMode |
|||
enum class AdaptiveClusteringMode : u32 { |
|||
StaticFov, |
|||
DynamicFov, |
|||
}; |
|||
|
|||
// This is nn::irsensor::AdaptiveClusteringTargetDistance |
|||
enum class AdaptiveClusteringTargetDistance : u32 { |
|||
Near, |
|||
Middle, |
|||
Far, |
|||
}; |
|||
|
|||
// This is nn::irsensor::ImageTransferProcessorFormat |
|||
enum class ImageTransferProcessorFormat : u32 { |
|||
Size320x240, |
|||
Size160x120, |
|||
Size80x60, |
|||
Size40x30, |
|||
Size20x15, |
|||
}; |
|||
|
|||
// This is nn::irsensor::PackedImageTransferProcessorFormat |
|||
enum class PackedImageTransferProcessorFormat : u8 { |
|||
Size320x240, |
|||
Size160x120, |
|||
Size80x60, |
|||
Size40x30, |
|||
Size20x15, |
|||
}; |
|||
|
|||
// This is nn::irsensor::IrCameraStatus |
|||
enum class IrCameraStatus : u32 { |
|||
Available, |
|||
Unsupported, |
|||
Unconnected, |
|||
}; |
|||
|
|||
// This is nn::irsensor::IrCameraInternalStatus |
|||
enum class IrCameraInternalStatus : u32 { |
|||
Stopped, |
|||
FirmwareUpdateNeeded, |
|||
Unkown2, |
|||
Unkown3, |
|||
Unkown4, |
|||
FirmwareVersionRequested, |
|||
FirmwareVersionIsInvalid, |
|||
Ready, |
|||
Setting, |
|||
}; |
|||
|
|||
// This is nn::irsensor::detail::StatusManager::IrSensorMode |
|||
enum class IrSensorMode : u64 { |
|||
None, |
|||
MomentProcessor, |
|||
ClusteringProcessor, |
|||
ImageTransferProcessor, |
|||
PointingProcessorMarker, |
|||
TeraPluginProcessor, |
|||
IrLedProcessor, |
|||
}; |
|||
|
|||
// This is nn::irsensor::ImageProcessorStatus |
|||
enum ImageProcessorStatus : u32 { |
|||
Stopped, |
|||
Running, |
|||
}; |
|||
|
|||
// This is nn::irsensor::HandAnalysisMode |
|||
enum class HandAnalysisMode : u32 { |
|||
None, |
|||
Silhouette, |
|||
Image, |
|||
SilhoueteAndImage, |
|||
SilhuetteOnly, |
|||
}; |
|||
|
|||
// This is nn::irsensor::IrSensorFunctionLevel |
|||
enum class IrSensorFunctionLevel : u8 { |
|||
unknown0, |
|||
unknown1, |
|||
unknown2, |
|||
unknown3, |
|||
unknown4, |
|||
}; |
|||
|
|||
// This is nn::irsensor::MomentProcessorPreprocess |
|||
enum class MomentProcessorPreprocess : u32 { |
|||
Unkown0, |
|||
Unkown1, |
|||
}; |
|||
|
|||
// This is nn::irsensor::PackedMomentProcessorPreprocess |
|||
enum class PackedMomentProcessorPreprocess : u8 { |
|||
Unkown0, |
|||
Unkown1, |
|||
}; |
|||
|
|||
// This is nn::irsensor::PointingStatus |
|||
enum class PointingStatus : u32 { |
|||
Unkown0, |
|||
Unkown1, |
|||
}; |
|||
|
|||
struct IrsRect { |
|||
s16 x; |
|||
s16 y; |
|||
s16 width; |
|||
s16 height; |
|||
}; |
|||
|
|||
struct IrsCentroid { |
|||
f32 x; |
|||
f32 y; |
|||
}; |
|||
|
|||
struct CameraConfig { |
|||
u64 exposure_time; |
|||
CameraLightTarget light_target; |
|||
u32 gain; |
|||
bool is_negative_used; |
|||
INSERT_PADDING_BYTES(7); |
|||
}; |
|||
static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size"); |
|||
|
|||
struct PackedCameraConfig { |
|||
u64 exposure_time; |
|||
PackedCameraLightTarget light_target; |
|||
u8 gain; |
|||
bool is_negative_used; |
|||
INSERT_PADDING_BYTES(5); |
|||
}; |
|||
static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::IrCameraHandle |
|||
struct IrCameraHandle { |
|||
u8 npad_id{}; |
|||
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; |
|||
INSERT_PADDING_BYTES(2); |
|||
}; |
|||
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedMcuVersion |
|||
struct PackedMcuVersion { |
|||
u16 major; |
|||
u16 minor; |
|||
}; |
|||
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedMomentProcessorConfig |
|||
struct PackedMomentProcessorConfig { |
|||
PackedCameraConfig camera_config; |
|||
IrsRect window_of_interest; |
|||
PackedMcuVersion required_mcu_version; |
|||
PackedMomentProcessorPreprocess preprocess; |
|||
u8 preprocess_intensity_threshold; |
|||
INSERT_PADDING_BYTES(2); |
|||
}; |
|||
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, |
|||
"PackedMomentProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedClusteringProcessorConfig |
|||
struct PackedClusteringProcessorConfig { |
|||
PackedCameraConfig camera_config; |
|||
IrsRect window_of_interest; |
|||
PackedMcuVersion required_mcu_version; |
|||
u32 pixel_count_min; |
|||
u32 pixel_count_max; |
|||
u8 object_intensity_min; |
|||
bool is_external_light_filter_enabled; |
|||
INSERT_PADDING_BYTES(2); |
|||
}; |
|||
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28, |
|||
"PackedClusteringProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedImageTransferProcessorConfig |
|||
struct PackedImageTransferProcessorConfig { |
|||
PackedCameraConfig camera_config; |
|||
PackedMcuVersion required_mcu_version; |
|||
PackedImageTransferProcessorFormat format; |
|||
INSERT_PADDING_BYTES(3); |
|||
}; |
|||
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, |
|||
"PackedImageTransferProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedTeraPluginProcessorConfig |
|||
struct PackedTeraPluginProcessorConfig { |
|||
PackedMcuVersion required_mcu_version; |
|||
u8 mode; |
|||
u8 unknown_1; |
|||
u8 unknown_2; |
|||
u8 unknown_3; |
|||
}; |
|||
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, |
|||
"PackedTeraPluginProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedPointingProcessorConfig |
|||
struct PackedPointingProcessorConfig { |
|||
IrsRect window_of_interest; |
|||
PackedMcuVersion required_mcu_version; |
|||
}; |
|||
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, |
|||
"PackedPointingProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedFunctionLevel |
|||
struct PackedFunctionLevel { |
|||
IrSensorFunctionLevel function_level; |
|||
INSERT_PADDING_BYTES(3); |
|||
}; |
|||
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedImageTransferProcessorExConfig |
|||
struct PackedImageTransferProcessorExConfig { |
|||
PackedCameraConfig camera_config; |
|||
PackedMcuVersion required_mcu_version; |
|||
PackedImageTransferProcessorFormat origin_format; |
|||
PackedImageTransferProcessorFormat trimming_format; |
|||
u16 trimming_start_x; |
|||
u16 trimming_start_y; |
|||
bool is_external_light_filter_enabled; |
|||
INSERT_PADDING_BYTES(5); |
|||
}; |
|||
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, |
|||
"PackedImageTransferProcessorExConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::PackedIrLedProcessorConfig |
|||
struct PackedIrLedProcessorConfig { |
|||
PackedMcuVersion required_mcu_version; |
|||
u8 light_target; |
|||
INSERT_PADDING_BYTES(3); |
|||
}; |
|||
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, |
|||
"PackedIrLedProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::HandAnalysisConfig |
|||
struct HandAnalysisConfig { |
|||
HandAnalysisMode mode; |
|||
}; |
|||
static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::detail::ProcessorState contents are different for each processor |
|||
struct ProcessorState { |
|||
std::array<u8, 0xE20> processor_raw_data{}; |
|||
}; |
|||
static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size"); |
|||
|
|||
// This is nn::irsensor::detail::DeviceFormat |
|||
struct DeviceFormat { |
|||
Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected}; |
|||
Core::IrSensor::IrCameraInternalStatus camera_internal_status{ |
|||
Core::IrSensor::IrCameraInternalStatus::Ready}; |
|||
Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None}; |
|||
ProcessorState state{}; |
|||
}; |
|||
static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size"); |
|||
|
|||
// This is nn::irsensor::ImageTransferProcessorState |
|||
struct ImageTransferProcessorState { |
|||
u64 sampling_number; |
|||
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
|||
INSERT_PADDING_BYTES(4); |
|||
}; |
|||
static_assert(sizeof(ImageTransferProcessorState) == 0x10, |
|||
"ImageTransferProcessorState is an invalid size"); |
|||
|
|||
} // namespace Core::IrSensor |
|||
@ -0,0 +1,34 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
|||
|
|||
namespace Service::IRS { |
|||
ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format) |
|||
: device(device_format) { |
|||
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; |
|||
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
|||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
|||
} |
|||
|
|||
ClusteringProcessor::~ClusteringProcessor() = default; |
|||
|
|||
void ClusteringProcessor::StartProcessor() {} |
|||
|
|||
void ClusteringProcessor::SuspendProcessor() {} |
|||
|
|||
void ClusteringProcessor::StopProcessor() {} |
|||
|
|||
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { |
|||
current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
|||
current_config.camera_config.gain = config.camera_config.gain; |
|||
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; |
|||
current_config.camera_config.light_target = |
|||
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); |
|||
current_config.pixel_count_min = config.pixel_count_min; |
|||
current_config.pixel_count_max = config.pixel_count_max; |
|||
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; |
|||
current_config.object_intensity_min = config.object_intensity_min; |
|||
} |
|||
|
|||
} // namespace Service::IRS
|
|||
@ -0,0 +1,74 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
#include "core/hid/irs_types.h" |
|||
#include "core/hle/service/hid/irsensor/processor_base.h" |
|||
|
|||
namespace Service::IRS { |
|||
class ClusteringProcessor final : public ProcessorBase { |
|||
public: |
|||
explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format); |
|||
~ClusteringProcessor() override; |
|||
|
|||
// Called when the processor is initialized |
|||
void StartProcessor() override; |
|||
|
|||
// Called when the processor is suspended |
|||
void SuspendProcessor() override; |
|||
|
|||
// Called when the processor is stopped |
|||
void StopProcessor() override; |
|||
|
|||
// Sets config parameters of the camera |
|||
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config); |
|||
|
|||
private: |
|||
// This is nn::irsensor::ClusteringProcessorConfig |
|||
struct ClusteringProcessorConfig { |
|||
Core::IrSensor::CameraConfig camera_config; |
|||
Core::IrSensor::IrsRect window_of_interest; |
|||
u32 pixel_count_min; |
|||
u32 pixel_count_max; |
|||
u32 object_intensity_min; |
|||
bool is_external_light_filter_enabled; |
|||
INSERT_PADDING_BYTES(3); |
|||
}; |
|||
static_assert(sizeof(ClusteringProcessorConfig) == 0x30, |
|||
"ClusteringProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::AdaptiveClusteringProcessorConfig |
|||
struct AdaptiveClusteringProcessorConfig { |
|||
Core::IrSensor::AdaptiveClusteringMode mode; |
|||
Core::IrSensor::AdaptiveClusteringTargetDistance target_distance; |
|||
}; |
|||
static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8, |
|||
"AdaptiveClusteringProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::ClusteringData |
|||
struct ClusteringData { |
|||
f32 average_intensity; |
|||
Core::IrSensor::IrsCentroid centroid; |
|||
u32 pixel_count; |
|||
Core::IrSensor::IrsRect bound; |
|||
}; |
|||
static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size"); |
|||
|
|||
// This is nn::irsensor::ClusteringProcessorState |
|||
struct ClusteringProcessorState { |
|||
s64 sampling_number; |
|||
u64 timestamp; |
|||
u8 object_count; |
|||
INSERT_PADDING_BYTES(3); |
|||
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
|||
std::array<ClusteringData, 0x10> data; |
|||
}; |
|||
static_assert(sizeof(ClusteringProcessorState) == 0x198, |
|||
"ClusteringProcessorState is an invalid size"); |
|||
|
|||
ClusteringProcessorConfig current_config{}; |
|||
Core::IrSensor::DeviceFormat& device; |
|||
}; |
|||
} // namespace Service::IRS |
|||
@ -0,0 +1,150 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include "core/hid/emulated_controller.h"
|
|||
#include "core/hid/hid_core.h"
|
|||
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
|
|||
|
|||
namespace Service::IRS { |
|||
ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_, |
|||
Core::IrSensor::DeviceFormat& device_format, |
|||
std::size_t npad_index) |
|||
: device{device_format} { |
|||
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); |
|||
|
|||
Core::HID::ControllerUpdateCallback engine_callback{ |
|||
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, |
|||
.is_npad_service = true, |
|||
}; |
|||
callback_key = npad_device->SetCallback(engine_callback); |
|||
|
|||
device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor; |
|||
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
|||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
|||
} |
|||
|
|||
ImageTransferProcessor::~ImageTransferProcessor() { |
|||
npad_device->DeleteCallback(callback_key); |
|||
}; |
|||
|
|||
void ImageTransferProcessor::StartProcessor() { |
|||
is_active = true; |
|||
device.camera_status = Core::IrSensor::IrCameraStatus::Available; |
|||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; |
|||
processor_state.sampling_number = 0; |
|||
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; |
|||
} |
|||
|
|||
void ImageTransferProcessor::SuspendProcessor() {} |
|||
|
|||
void ImageTransferProcessor::StopProcessor() {} |
|||
|
|||
void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { |
|||
if (type != Core::HID::ControllerTriggerType::IrSensor) { |
|||
return; |
|||
} |
|||
if (!is_transfer_memory_set) { |
|||
return; |
|||
} |
|||
|
|||
const auto camera_data = npad_device->GetCamera(); |
|||
|
|||
// This indicates how much ambient light is precent
|
|||
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; |
|||
processor_state.sampling_number = camera_data.sample; |
|||
|
|||
if (camera_data.format != current_config.origin_format) { |
|||
LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format, |
|||
current_config.origin_format); |
|||
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); |
|||
return; |
|||
} |
|||
|
|||
if (current_config.origin_format > current_config.trimming_format) { |
|||
LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}", |
|||
current_config.origin_format, current_config.trimming_format); |
|||
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); |
|||
return; |
|||
} |
|||
|
|||
std::vector<u8> window_data{}; |
|||
const auto origin_width = GetDataWidth(current_config.origin_format); |
|||
const auto origin_height = GetDataHeight(current_config.origin_format); |
|||
const auto trimming_width = GetDataWidth(current_config.trimming_format); |
|||
const auto trimming_height = GetDataHeight(current_config.trimming_format); |
|||
window_data.resize(GetDataSize(current_config.trimming_format)); |
|||
|
|||
if (trimming_width + current_config.trimming_start_x > origin_width || |
|||
trimming_height + current_config.trimming_start_y > origin_height) { |
|||
LOG_WARNING(Service_IRS, |
|||
"Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})", |
|||
current_config.trimming_start_x, current_config.trimming_start_y, |
|||
trimming_width, trimming_height, origin_width, origin_height); |
|||
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); |
|||
return; |
|||
} |
|||
|
|||
for (std::size_t y = 0; y < trimming_height; y++) { |
|||
for (std::size_t x = 0; x < trimming_width; x++) { |
|||
const std::size_t window_index = (y * trimming_width) + x; |
|||
const std::size_t origin_index = |
|||
((y + current_config.trimming_start_y) * origin_width) + x + |
|||
current_config.trimming_start_x; |
|||
window_data[window_index] = camera_data.data[origin_index]; |
|||
} |
|||
} |
|||
|
|||
memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format)); |
|||
|
|||
if (!IsProcessorActive()) { |
|||
StartProcessor(); |
|||
} |
|||
} |
|||
|
|||
void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) { |
|||
current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
|||
current_config.camera_config.gain = config.camera_config.gain; |
|||
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; |
|||
current_config.camera_config.light_target = |
|||
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); |
|||
current_config.origin_format = |
|||
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); |
|||
current_config.trimming_format = |
|||
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); |
|||
current_config.trimming_start_x = 0; |
|||
current_config.trimming_start_y = 0; |
|||
|
|||
npad_device->SetCameraFormat(current_config.origin_format); |
|||
} |
|||
|
|||
void ImageTransferProcessor::SetConfig( |
|||
Core::IrSensor::PackedImageTransferProcessorExConfig config) { |
|||
current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
|||
current_config.camera_config.gain = config.camera_config.gain; |
|||
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; |
|||
current_config.camera_config.light_target = |
|||
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); |
|||
current_config.origin_format = |
|||
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format); |
|||
current_config.trimming_format = |
|||
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format); |
|||
current_config.trimming_start_x = config.trimming_start_x; |
|||
current_config.trimming_start_y = config.trimming_start_y; |
|||
|
|||
npad_device->SetCameraFormat(current_config.origin_format); |
|||
} |
|||
|
|||
void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) { |
|||
is_transfer_memory_set = true; |
|||
transfer_memory = t_mem; |
|||
} |
|||
|
|||
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( |
|||
std::vector<u8>& data) const { |
|||
const auto size = GetDataSize(current_config.trimming_format); |
|||
data.resize(size); |
|||
memcpy(data.data(), transfer_memory, size); |
|||
return processor_state; |
|||
} |
|||
|
|||
} // namespace Service::IRS
|
|||
@ -0,0 +1,73 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
#include "core/hid/irs_types.h" |
|||
#include "core/hle/service/hid/irsensor/processor_base.h" |
|||
|
|||
namespace Core::HID { |
|||
class EmulatedController; |
|||
} // namespace Core::HID |
|||
|
|||
namespace Service::IRS { |
|||
class ImageTransferProcessor final : public ProcessorBase { |
|||
public: |
|||
explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_, |
|||
Core::IrSensor::DeviceFormat& device_format, |
|||
std::size_t npad_index); |
|||
~ImageTransferProcessor() override; |
|||
|
|||
// Called when the processor is initialized |
|||
void StartProcessor() override; |
|||
|
|||
// Called when the processor is suspended |
|||
void SuspendProcessor() override; |
|||
|
|||
// Called when the processor is stopped |
|||
void StopProcessor() override; |
|||
|
|||
// Sets config parameters of the camera |
|||
void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config); |
|||
void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config); |
|||
|
|||
// Transfer memory where the image data will be stored |
|||
void SetTransferMemoryPointer(u8* t_mem); |
|||
|
|||
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; |
|||
|
|||
private: |
|||
// This is nn::irsensor::ImageTransferProcessorConfig |
|||
struct ImageTransferProcessorConfig { |
|||
Core::IrSensor::CameraConfig camera_config; |
|||
Core::IrSensor::ImageTransferProcessorFormat format; |
|||
}; |
|||
static_assert(sizeof(ImageTransferProcessorConfig) == 0x20, |
|||
"ImageTransferProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::ImageTransferProcessorExConfig |
|||
struct ImageTransferProcessorExConfig { |
|||
Core::IrSensor::CameraConfig camera_config; |
|||
Core::IrSensor::ImageTransferProcessorFormat origin_format; |
|||
Core::IrSensor::ImageTransferProcessorFormat trimming_format; |
|||
u16 trimming_start_x; |
|||
u16 trimming_start_y; |
|||
bool is_external_light_filter_enabled; |
|||
INSERT_PADDING_BYTES(3); |
|||
}; |
|||
static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28, |
|||
"ImageTransferProcessorExConfig is an invalid size"); |
|||
|
|||
void OnControllerUpdate(Core::HID::ControllerTriggerType type); |
|||
|
|||
ImageTransferProcessorExConfig current_config{}; |
|||
Core::IrSensor::ImageTransferProcessorState processor_state{}; |
|||
Core::IrSensor::DeviceFormat& device; |
|||
Core::HID::EmulatedController* npad_device; |
|||
int callback_key{}; |
|||
|
|||
u8* transfer_memory = nullptr; |
|||
bool is_transfer_memory_set = false; |
|||
}; |
|||
} // namespace Service::IRS |
|||
@ -0,0 +1,27 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
|
|||
|
|||
namespace Service::IRS { |
|||
IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format) |
|||
: device(device_format) { |
|||
device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor; |
|||
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
|||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
|||
} |
|||
|
|||
IrLedProcessor::~IrLedProcessor() = default; |
|||
|
|||
void IrLedProcessor::StartProcessor() {} |
|||
|
|||
void IrLedProcessor::SuspendProcessor() {} |
|||
|
|||
void IrLedProcessor::StopProcessor() {} |
|||
|
|||
void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) { |
|||
current_config.light_target = |
|||
static_cast<Core::IrSensor::CameraLightTarget>(config.light_target); |
|||
} |
|||
|
|||
} // namespace Service::IRS
|
|||
@ -0,0 +1,47 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/bit_field.h" |
|||
#include "common/common_types.h" |
|||
#include "core/hid/irs_types.h" |
|||
#include "core/hle/service/hid/irsensor/processor_base.h" |
|||
|
|||
namespace Service::IRS { |
|||
class IrLedProcessor final : public ProcessorBase { |
|||
public: |
|||
explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format); |
|||
~IrLedProcessor() override; |
|||
|
|||
// Called when the processor is initialized |
|||
void StartProcessor() override; |
|||
|
|||
// Called when the processor is suspended |
|||
void SuspendProcessor() override; |
|||
|
|||
// Called when the processor is stopped |
|||
void StopProcessor() override; |
|||
|
|||
// Sets config parameters of the camera |
|||
void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config); |
|||
|
|||
private: |
|||
// This is nn::irsensor::IrLedProcessorConfig |
|||
struct IrLedProcessorConfig { |
|||
Core::IrSensor::CameraLightTarget light_target; |
|||
}; |
|||
static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size"); |
|||
|
|||
struct IrLedProcessorState { |
|||
s64 sampling_number; |
|||
u64 timestamp; |
|||
std::array<u8, 0x8> data; |
|||
}; |
|||
static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size"); |
|||
|
|||
IrLedProcessorConfig current_config{}; |
|||
Core::IrSensor::DeviceFormat& device; |
|||
}; |
|||
|
|||
} // namespace Service::IRS |
|||
@ -0,0 +1,34 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include "core/hle/service/hid/irsensor/moment_processor.h"
|
|||
|
|||
namespace Service::IRS { |
|||
MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) |
|||
: device(device_format) { |
|||
device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; |
|||
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
|||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
|||
} |
|||
|
|||
MomentProcessor::~MomentProcessor() = default; |
|||
|
|||
void MomentProcessor::StartProcessor() {} |
|||
|
|||
void MomentProcessor::SuspendProcessor() {} |
|||
|
|||
void MomentProcessor::StopProcessor() {} |
|||
|
|||
void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { |
|||
current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
|||
current_config.camera_config.gain = config.camera_config.gain; |
|||
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; |
|||
current_config.camera_config.light_target = |
|||
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); |
|||
current_config.window_of_interest = config.window_of_interest; |
|||
current_config.preprocess = |
|||
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); |
|||
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; |
|||
} |
|||
|
|||
} // namespace Service::IRS
|
|||
@ -0,0 +1,61 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/bit_field.h" |
|||
#include "common/common_types.h" |
|||
#include "core/hid/irs_types.h" |
|||
#include "core/hle/service/hid/irsensor/processor_base.h" |
|||
|
|||
namespace Service::IRS { |
|||
class MomentProcessor final : public ProcessorBase { |
|||
public: |
|||
explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); |
|||
~MomentProcessor() override; |
|||
|
|||
// Called when the processor is initialized |
|||
void StartProcessor() override; |
|||
|
|||
// Called when the processor is suspended |
|||
void SuspendProcessor() override; |
|||
|
|||
// Called when the processor is stopped |
|||
void StopProcessor() override; |
|||
|
|||
// Sets config parameters of the camera |
|||
void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); |
|||
|
|||
private: |
|||
// This is nn::irsensor::MomentProcessorConfig |
|||
struct MomentProcessorConfig { |
|||
Core::IrSensor::CameraConfig camera_config; |
|||
Core::IrSensor::IrsRect window_of_interest; |
|||
Core::IrSensor::MomentProcessorPreprocess preprocess; |
|||
u32 preprocess_intensity_threshold; |
|||
}; |
|||
static_assert(sizeof(MomentProcessorConfig) == 0x28, |
|||
"MomentProcessorConfig is an invalid size"); |
|||
|
|||
// This is nn::irsensor::MomentStatistic |
|||
struct MomentStatistic { |
|||
f32 average_intensity; |
|||
Core::IrSensor::IrsCentroid centroid; |
|||
}; |
|||
static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size"); |
|||
|
|||
// This is nn::irsensor::MomentProcessorState |
|||
struct MomentProcessorState { |
|||
s64 sampling_number; |
|||
u64 timestamp; |
|||
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
|||
INSERT_PADDING_BYTES(4); |
|||
std::array<MomentStatistic, 0x30> stadistic; |
|||
}; |
|||
static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); |
|||
|
|||
MomentProcessorConfig current_config{}; |
|||
Core::IrSensor::DeviceFormat& device; |
|||
}; |
|||
|
|||
} // namespace Service::IRS |
|||
@ -0,0 +1,26 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include "core/hle/service/hid/irsensor/pointing_processor.h"
|
|||
|
|||
namespace Service::IRS { |
|||
PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format) |
|||
: device(device_format) { |
|||
device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker; |
|||
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
|||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
|||
} |
|||
|
|||
PointingProcessor::~PointingProcessor() = default; |
|||
|
|||
void PointingProcessor::StartProcessor() {} |
|||
|
|||
void PointingProcessor::SuspendProcessor() {} |
|||
|
|||
void PointingProcessor::StopProcessor() {} |
|||
|
|||
void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) { |
|||
current_config.window_of_interest = config.window_of_interest; |
|||
} |
|||
|
|||
} // namespace Service::IRS
|
|||
@ -0,0 +1,61 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
#include "core/hid/irs_types.h" |
|||
#include "core/hle/service/hid/irsensor/processor_base.h" |
|||
|
|||
namespace Service::IRS { |
|||
class PointingProcessor final : public ProcessorBase { |
|||
public: |
|||
explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format); |
|||
~PointingProcessor() override; |
|||
|
|||
// Called when the processor is initialized |
|||
void StartProcessor() override; |
|||
|
|||
// Called when the processor is suspended |
|||
void SuspendProcessor() override; |
|||
|
|||
// Called when the processor is stopped |
|||
void StopProcessor() override; |
|||
|
|||
// Sets config parameters of the camera |
|||
void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config); |
|||
|
|||
private: |
|||
// This is nn::irsensor::PointingProcessorConfig |
|||
struct PointingProcessorConfig { |
|||
Core::IrSensor::IrsRect window_of_interest; |
|||
}; |
|||
static_assert(sizeof(PointingProcessorConfig) == 0x8, |
|||
"PointingProcessorConfig is an invalid size"); |
|||
|
|||
struct PointingProcessorMarkerData { |
|||
u8 pointing_status; |
|||
INSERT_PADDING_BYTES(3); |
|||
u32 unknown; |
|||
float unkown_float1; |
|||
float position_x; |
|||
float position_y; |
|||
float unkown_float2; |
|||
Core::IrSensor::IrsRect window_of_interest; |
|||
}; |
|||
static_assert(sizeof(PointingProcessorMarkerData) == 0x20, |
|||
"PointingProcessorMarkerData is an invalid size"); |
|||
|
|||
struct PointingProcessorMarkerState { |
|||
s64 sampling_number; |
|||
u64 timestamp; |
|||
std::array<PointingProcessorMarkerData, 0x3> data; |
|||
}; |
|||
static_assert(sizeof(PointingProcessorMarkerState) == 0x70, |
|||
"PointingProcessorMarkerState is an invalid size"); |
|||
|
|||
PointingProcessorConfig current_config{}; |
|||
Core::IrSensor::DeviceFormat& device; |
|||
}; |
|||
|
|||
} // namespace Service::IRS |
|||
@ -0,0 +1,67 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include "core/hle/service/hid/irsensor/processor_base.h"
|
|||
|
|||
namespace Service::IRS { |
|||
|
|||
ProcessorBase::ProcessorBase() {} |
|||
ProcessorBase::~ProcessorBase() = default; |
|||
|
|||
bool ProcessorBase::IsProcessorActive() const { |
|||
return is_active; |
|||
} |
|||
|
|||
std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const { |
|||
switch (format) { |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: |
|||
return 320 * 240; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: |
|||
return 160 * 120; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: |
|||
return 80 * 60; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: |
|||
return 40 * 30; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: |
|||
return 20 * 15; |
|||
default: |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const { |
|||
switch (format) { |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: |
|||
return 320; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: |
|||
return 160; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: |
|||
return 80; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: |
|||
return 40; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: |
|||
return 20; |
|||
default: |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
std::size_t ProcessorBase::GetDataHeight( |
|||
Core::IrSensor::ImageTransferProcessorFormat format) const { |
|||
switch (format) { |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: |
|||
return 240; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: |
|||
return 120; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: |
|||
return 60; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: |
|||
return 30; |
|||
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: |
|||
return 15; |
|||
default: |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
} // namespace Service::IRS
|
|||
@ -0,0 +1,33 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
#include "core/hid/irs_types.h" |
|||
|
|||
namespace Service::IRS { |
|||
class ProcessorBase { |
|||
public: |
|||
explicit ProcessorBase(); |
|||
virtual ~ProcessorBase(); |
|||
|
|||
virtual void StartProcessor() = 0; |
|||
virtual void SuspendProcessor() = 0; |
|||
virtual void StopProcessor() = 0; |
|||
|
|||
bool IsProcessorActive() const; |
|||
|
|||
protected: |
|||
/// Returns the number of bytes the image uses |
|||
std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const; |
|||
|
|||
/// Returns the width of the image |
|||
std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const; |
|||
|
|||
/// Returns the height of the image |
|||
std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const; |
|||
|
|||
bool is_active{false}; |
|||
}; |
|||
} // namespace Service::IRS |
|||
@ -0,0 +1,29 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
|
|||
|
|||
namespace Service::IRS { |
|||
TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format) |
|||
: device(device_format) { |
|||
device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor; |
|||
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
|||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
|||
} |
|||
|
|||
TeraPluginProcessor::~TeraPluginProcessor() = default; |
|||
|
|||
void TeraPluginProcessor::StartProcessor() {} |
|||
|
|||
void TeraPluginProcessor::SuspendProcessor() {} |
|||
|
|||
void TeraPluginProcessor::StopProcessor() {} |
|||
|
|||
void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) { |
|||
current_config.mode = config.mode; |
|||
current_config.unknown_1 = config.unknown_1; |
|||
current_config.unknown_2 = config.unknown_2; |
|||
current_config.unknown_3 = config.unknown_3; |
|||
} |
|||
|
|||
} // namespace Service::IRS
|
|||
@ -0,0 +1,53 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/bit_field.h" |
|||
#include "common/common_types.h" |
|||
#include "core/hid/irs_types.h" |
|||
#include "core/hle/service/hid/irsensor/processor_base.h" |
|||
|
|||
namespace Service::IRS { |
|||
class TeraPluginProcessor final : public ProcessorBase { |
|||
public: |
|||
explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format); |
|||
~TeraPluginProcessor() override; |
|||
|
|||
// Called when the processor is initialized |
|||
void StartProcessor() override; |
|||
|
|||
// Called when the processor is suspended |
|||
void SuspendProcessor() override; |
|||
|
|||
// Called when the processor is stopped |
|||
void StopProcessor() override; |
|||
|
|||
// Sets config parameters of the camera |
|||
void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config); |
|||
|
|||
private: |
|||
// This is nn::irsensor::TeraPluginProcessorConfig |
|||
struct TeraPluginProcessorConfig { |
|||
u8 mode; |
|||
u8 unknown_1; |
|||
u8 unknown_2; |
|||
u8 unknown_3; |
|||
}; |
|||
static_assert(sizeof(TeraPluginProcessorConfig) == 0x4, |
|||
"TeraPluginProcessorConfig is an invalid size"); |
|||
|
|||
struct TeraPluginProcessorState { |
|||
s64 sampling_number; |
|||
u64 timestamp; |
|||
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
|||
std::array<u8, 0x12c> data; |
|||
}; |
|||
static_assert(sizeof(TeraPluginProcessorState) == 0x140, |
|||
"TeraPluginProcessorState is an invalid size"); |
|||
|
|||
TeraPluginProcessorConfig current_config{}; |
|||
Core::IrSensor::DeviceFormat& device; |
|||
}; |
|||
|
|||
} // namespace Service::IRS |
|||
@ -0,0 +1,82 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include <fmt/format.h>
|
|||
|
|||
#include "common/param_package.h"
|
|||
#include "input_common/drivers/camera.h"
|
|||
|
|||
namespace InputCommon { |
|||
constexpr PadIdentifier identifier = { |
|||
.guid = Common::UUID{}, |
|||
.port = 0, |
|||
.pad = 0, |
|||
}; |
|||
|
|||
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) { |
|||
PreSetController(identifier); |
|||
} |
|||
|
|||
void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) { |
|||
const std::size_t desired_width = getImageWidth(); |
|||
const std::size_t desired_height = getImageHeight(); |
|||
status.data.resize(desired_width * desired_height); |
|||
|
|||
// Resize image to desired format
|
|||
for (std::size_t y = 0; y < desired_height; y++) { |
|||
for (std::size_t x = 0; x < desired_width; x++) { |
|||
const std::size_t pixel_index = y * desired_width + x; |
|||
const std::size_t old_x = width * x / desired_width; |
|||
const std::size_t old_y = height * y / desired_height; |
|||
const std::size_t data_pixel_index = old_y * width + old_x; |
|||
status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF); |
|||
} |
|||
} |
|||
|
|||
SetCamera(identifier, status); |
|||
} |
|||
|
|||
std::size_t Camera::getImageWidth() const { |
|||
switch (status.format) { |
|||
case Common::Input::CameraFormat::Size320x240: |
|||
return 320; |
|||
case Common::Input::CameraFormat::Size160x120: |
|||
return 160; |
|||
case Common::Input::CameraFormat::Size80x60: |
|||
return 80; |
|||
case Common::Input::CameraFormat::Size40x30: |
|||
return 40; |
|||
case Common::Input::CameraFormat::Size20x15: |
|||
return 20; |
|||
case Common::Input::CameraFormat::None: |
|||
default: |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
std::size_t Camera::getImageHeight() const { |
|||
switch (status.format) { |
|||
case Common::Input::CameraFormat::Size320x240: |
|||
return 240; |
|||
case Common::Input::CameraFormat::Size160x120: |
|||
return 120; |
|||
case Common::Input::CameraFormat::Size80x60: |
|||
return 60; |
|||
case Common::Input::CameraFormat::Size40x30: |
|||
return 30; |
|||
case Common::Input::CameraFormat::Size20x15: |
|||
return 15; |
|||
case Common::Input::CameraFormat::None: |
|||
default: |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
Common::Input::CameraError Camera::SetCameraFormat( |
|||
[[maybe_unused]] const PadIdentifier& identifier_, |
|||
const Common::Input::CameraFormat camera_format) { |
|||
status.format = camera_format; |
|||
return Common::Input::CameraError::None; |
|||
} |
|||
|
|||
} // namespace InputCommon
|
|||
@ -0,0 +1,29 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "input_common/input_engine.h" |
|||
|
|||
namespace InputCommon { |
|||
|
|||
/** |
|||
* A button device factory representing a keyboard. It receives keyboard events and forward them |
|||
* to all button devices it created. |
|||
*/ |
|||
class Camera final : public InputEngine { |
|||
public: |
|||
explicit Camera(std::string input_engine_); |
|||
|
|||
void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data); |
|||
|
|||
std::size_t getImageWidth() const; |
|||
std::size_t getImageHeight() const; |
|||
|
|||
Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, |
|||
Common::Input::CameraFormat camera_format) override; |
|||
|
|||
Common::Input::CameraStatus status{}; |
|||
}; |
|||
|
|||
} // namespace InputCommon |
|||
@ -0,0 +1,138 @@ |
|||
// Text : Copyright 2022 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include <memory>
|
|||
#include <QCameraImageCapture>
|
|||
#include <QCameraInfo>
|
|||
#include <QStandardItemModel>
|
|||
#include <QTimer>
|
|||
|
|||
#include "input_common/drivers/camera.h"
|
|||
#include "input_common/main.h"
|
|||
#include "ui_configure_camera.h"
|
|||
#include "yuzu/configuration/config.h"
|
|||
#include "yuzu/configuration/configure_camera.h"
|
|||
|
|||
ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) |
|||
: QDialog(parent), input_subsystem{input_subsystem_}, |
|||
ui(std::make_unique<Ui::ConfigureCamera>()) { |
|||
ui->setupUi(this); |
|||
|
|||
connect(ui->restore_defaults_button, &QPushButton::clicked, this, |
|||
&ConfigureCamera::RestoreDefaults); |
|||
connect(ui->preview_button, &QPushButton::clicked, this, &ConfigureCamera::PreviewCamera); |
|||
|
|||
auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32); |
|||
blank_image.fill(Qt::black); |
|||
DisplayCapturedFrame(0, blank_image); |
|||
|
|||
LoadConfiguration(); |
|||
resize(0, 0); |
|||
} |
|||
|
|||
ConfigureCamera::~ConfigureCamera() = default; |
|||
|
|||
void ConfigureCamera::PreviewCamera() { |
|||
const auto index = ui->ir_sensor_combo_box->currentIndex(); |
|||
bool camera_found = false; |
|||
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); |
|||
for (const QCameraInfo& cameraInfo : cameras) { |
|||
if (input_devices[index] == cameraInfo.deviceName().toStdString() || |
|||
input_devices[index] == "Auto") { |
|||
LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(), |
|||
cameraInfo.deviceName().toStdString()); |
|||
camera = std::make_unique<QCamera>(cameraInfo); |
|||
camera_found = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// Clear previous frame
|
|||
auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32); |
|||
blank_image.fill(Qt::black); |
|||
DisplayCapturedFrame(0, blank_image); |
|||
|
|||
if (!camera_found) { |
|||
return; |
|||
} |
|||
|
|||
camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); |
|||
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, |
|||
&ConfigureCamera::DisplayCapturedFrame); |
|||
camera->unload(); |
|||
camera->setCaptureMode(QCamera::CaptureViewfinder); |
|||
camera->load(); |
|||
camera->start(); |
|||
|
|||
pending_snapshots = 0; |
|||
is_virtual_camera = false; |
|||
|
|||
camera_timer = std::make_unique<QTimer>(); |
|||
connect(camera_timer.get(), &QTimer::timeout, [this] { |
|||
// If the camera doesn't capture, test for virtual cameras
|
|||
if (pending_snapshots > 5) { |
|||
is_virtual_camera = true; |
|||
} |
|||
// Virtual cameras like obs need to reset the camera every capture
|
|||
if (is_virtual_camera) { |
|||
camera->stop(); |
|||
camera->start(); |
|||
} |
|||
pending_snapshots++; |
|||
camera_capture->capture(); |
|||
}); |
|||
|
|||
camera_timer->start(250); |
|||
} |
|||
|
|||
void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) { |
|||
LOG_INFO(Frontend, "ImageCaptured {} {}", img.width(), img.height()); |
|||
const auto converted = img.scaled(320, 240, Qt::AspectRatioMode::IgnoreAspectRatio, |
|||
Qt::TransformationMode::SmoothTransformation); |
|||
ui->preview_box->setPixmap(QPixmap::fromImage(converted)); |
|||
pending_snapshots = 0; |
|||
} |
|||
|
|||
void ConfigureCamera::changeEvent(QEvent* event) { |
|||
if (event->type() == QEvent::LanguageChange) { |
|||
RetranslateUI(); |
|||
} |
|||
|
|||
QDialog::changeEvent(event); |
|||
} |
|||
|
|||
void ConfigureCamera::RetranslateUI() { |
|||
ui->retranslateUi(this); |
|||
} |
|||
|
|||
void ConfigureCamera::ApplyConfiguration() { |
|||
const auto index = ui->ir_sensor_combo_box->currentIndex(); |
|||
Settings::values.ir_sensor_device.SetValue(input_devices[index]); |
|||
} |
|||
|
|||
void ConfigureCamera::LoadConfiguration() { |
|||
input_devices.clear(); |
|||
ui->ir_sensor_combo_box->clear(); |
|||
input_devices.push_back("Auto"); |
|||
ui->ir_sensor_combo_box->addItem(tr("Auto")); |
|||
const auto cameras = QCameraInfo::availableCameras(); |
|||
for (const QCameraInfo& cameraInfo : cameras) { |
|||
input_devices.push_back(cameraInfo.deviceName().toStdString()); |
|||
ui->ir_sensor_combo_box->addItem(cameraInfo.description()); |
|||
} |
|||
|
|||
const auto current_device = Settings::values.ir_sensor_device.GetValue(); |
|||
|
|||
const auto devices_it = std::find_if( |
|||
input_devices.begin(), input_devices.end(), |
|||
[current_device](const std::string& device) { return device == current_device; }); |
|||
const int device_index = |
|||
devices_it != input_devices.end() |
|||
? static_cast<int>(std::distance(input_devices.begin(), devices_it)) |
|||
: 0; |
|||
ui->ir_sensor_combo_box->setCurrentIndex(device_index); |
|||
} |
|||
|
|||
void ConfigureCamera::RestoreDefaults() { |
|||
ui->ir_sensor_combo_box->setCurrentIndex(0); |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
// Text : Copyright 2022 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <QDialog> |
|||
|
|||
class QTimer; |
|||
class QCamera; |
|||
class QCameraImageCapture; |
|||
|
|||
namespace InputCommon { |
|||
class InputSubsystem; |
|||
} // namespace InputCommon |
|||
|
|||
namespace Ui { |
|||
class ConfigureCamera; |
|||
} |
|||
|
|||
class ConfigureCamera : public QDialog { |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
explicit ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_); |
|||
~ConfigureCamera() override; |
|||
|
|||
void ApplyConfiguration(); |
|||
|
|||
private: |
|||
void changeEvent(QEvent* event) override; |
|||
void RetranslateUI(); |
|||
|
|||
/// Load configuration settings. |
|||
void LoadConfiguration(); |
|||
|
|||
/// Restore all buttons to their default values. |
|||
void RestoreDefaults(); |
|||
|
|||
void DisplayCapturedFrame(int requestId, const QImage& img); |
|||
|
|||
/// Loads and signals the current selected camera to display a frame |
|||
void PreviewCamera(); |
|||
|
|||
InputCommon::InputSubsystem* input_subsystem; |
|||
|
|||
bool is_virtual_camera; |
|||
int pending_snapshots; |
|||
std::unique_ptr<QCamera> camera; |
|||
std::unique_ptr<QCameraImageCapture> camera_capture; |
|||
std::unique_ptr<QTimer> camera_timer; |
|||
std::vector<std::string> input_devices; |
|||
std::unique_ptr<Ui::ConfigureCamera> ui; |
|||
}; |
|||
@ -0,0 +1,170 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>ConfigureCamera</class> |
|||
<widget class="QDialog" name="ConfigureCamera"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>298</width> |
|||
<height>339</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Configure Infrared Camera</string> |
|||
</property> |
|||
<layout class="QVBoxLayout" name="verticalLayout"> |
|||
<item> |
|||
<widget class="QLabel" name="label_2"> |
|||
<property name="minimumSize"> |
|||
<size> |
|||
<width>280</width> |
|||
<height>0</height> |
|||
</size> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera.</string> |
|||
</property> |
|||
<property name="wordWrap"> |
|||
<bool>true</bool> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<spacer name="verticalSpacer_2"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<property name="sizeType"> |
|||
<enum>QSizePolicy::Fixed</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>20</width> |
|||
<height>20</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item> |
|||
<widget class="QGroupBox" name="gridGroupBox"> |
|||
<property name="title"> |
|||
<string>Camera Image Source:</string> |
|||
</property> |
|||
<layout class="QGridLayout" name="gridLayout"> |
|||
<item row="0" column="0"> |
|||
<spacer name="horizontalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>40</width> |
|||
<height>20</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item row="0" column="1"> |
|||
<widget class="QLabel" name="label_3"> |
|||
<property name="text"> |
|||
<string>Input device:</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="0" column="2"> |
|||
<widget class="QComboBox" name="ir_sensor_combo_box"/> |
|||
</item> |
|||
<item row="0" column="3"> |
|||
<spacer name="horizontalSpacer_2"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>40</width> |
|||
<height>20</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item><item> |
|||
<widget class="QGroupBox" name="previewBox"> |
|||
<property name="title"> |
|||
<string>Preview</string> |
|||
</property> |
|||
<layout class="QVBoxLayout" name="verticalLayout_3"> |
|||
<item> |
|||
<widget class="QLabel" name="preview_box"> |
|||
<property name="minimumSize"> |
|||
<size> |
|||
<width>320</width> |
|||
<height>240</height> |
|||
</size> |
|||
</property> |
|||
<property name="toolTip"> |
|||
<string>Resolution: 320*240</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QPushButton" name="preview_button"> |
|||
<property name="text"> |
|||
<string>Click to preview</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<spacer name="verticalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>20</width> |
|||
<height>40</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item> |
|||
<layout class="QHBoxLayout" name="horizontalLayout"> |
|||
<item> |
|||
<widget class="QPushButton" name="restore_defaults_button"> |
|||
<property name="text"> |
|||
<string>Restore Defaults</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QDialogButtonBox" name="buttonBox"> |
|||
<property name="standardButtons"> |
|||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<resources/> |
|||
<connections> |
|||
<connection> |
|||
<sender>buttonBox</sender> |
|||
<signal>accepted()</signal> |
|||
<receiver>ConfigureCamera</receiver> |
|||
<slot>accept()</slot> |
|||
</connection> |
|||
<connection> |
|||
<sender>buttonBox</sender> |
|||
<signal>rejected()</signal> |
|||
<receiver>ConfigureCamera</receiver> |
|||
<slot>reject()</slot> |
|||
</connection> |
|||
</connections> |
|||
</ui> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue