mirror of
https://github.com/yuzu-mirror/yuzu.git
synced 2024-11-18 06:47:34 +01:00
service: irs: Implement clustering processor
This commit is contained in:
parent
5af06d1433
commit
3ac4f3a252
@ -446,6 +446,7 @@ add_library(core STATIC
|
|||||||
hle/service/hid/hidbus.h
|
hle/service/hid/hidbus.h
|
||||||
hle/service/hid/irs.cpp
|
hle/service/hid/irs.cpp
|
||||||
hle/service/hid/irs.h
|
hle/service/hid/irs.h
|
||||||
|
hle/service/hid/irs_ring_lifo.h
|
||||||
hle/service/hid/ring_lifo.h
|
hle/service/hid/ring_lifo.h
|
||||||
hle/service/hid/xcd.cpp
|
hle/service/hid/xcd.cpp
|
||||||
hle/service/hid/xcd.h
|
hle/service/hid/xcd.h
|
||||||
|
@ -166,7 +166,7 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
if (result.IsSuccess()) {
|
if (result.IsSuccess()) {
|
||||||
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device);
|
MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
|
||||||
auto& image_transfer_processor =
|
auto& image_transfer_processor =
|
||||||
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
|
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
|
||||||
image_transfer_processor.SetConfig(parameters.processor_config);
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
47
src/core/hle/service/hid/irs_ring_lifo.h
Normal file
47
src/core/hle/service/hid/irs_ring_lifo.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
|
||||||
|
template <typename State, std::size_t max_buffer_size>
|
||||||
|
struct Lifo {
|
||||||
|
s64 sampling_number{};
|
||||||
|
s64 buffer_count{};
|
||||||
|
std::array<State, max_buffer_size> entries{};
|
||||||
|
|
||||||
|
const State& ReadCurrentEntry() const {
|
||||||
|
return entries[GetBufferTail()];
|
||||||
|
}
|
||||||
|
|
||||||
|
const State& ReadPreviousEntry() const {
|
||||||
|
return entries[GetPreviousEntryIndex()];
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 GetBufferTail() const {
|
||||||
|
return sampling_number % max_buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetPreviousEntryIndex() const {
|
||||||
|
return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetNextEntryIndex() const {
|
||||||
|
return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteNextEntry(const State& new_state) {
|
||||||
|
if (buffer_count < max_buffer_size) {
|
||||||
|
buffer_count++;
|
||||||
|
}
|
||||||
|
sampling_number++;
|
||||||
|
entries[GetBufferTail()] = new_state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
@ -1,34 +1,263 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "core/hid/emulated_controller.h"
|
||||||
|
#include "core/hid/hid_core.h"
|
||||||
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
||||||
|
|
||||||
namespace Service::IRS {
|
namespace Service::IRS {
|
||||||
ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format)
|
ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
|
||||||
: device(device_format) {
|
Core::IrSensor::DeviceFormat& device_format,
|
||||||
|
std::size_t npad_index)
|
||||||
|
: device{device_format} {
|
||||||
|
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
|
||||||
|
|
||||||
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
|
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
|
||||||
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
||||||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
||||||
|
SetDefaultConfig();
|
||||||
|
|
||||||
|
shared_memory = std::construct_at(
|
||||||
|
reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClusteringProcessor::~ClusteringProcessor() = default;
|
ClusteringProcessor::~ClusteringProcessor() {
|
||||||
|
npad_device->DeleteCallback(callback_key);
|
||||||
|
};
|
||||||
|
|
||||||
void ClusteringProcessor::StartProcessor() {}
|
void ClusteringProcessor::StartProcessor() {
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
|
||||||
|
}
|
||||||
|
|
||||||
void ClusteringProcessor::SuspendProcessor() {}
|
void ClusteringProcessor::SuspendProcessor() {}
|
||||||
|
|
||||||
void ClusteringProcessor::StopProcessor() {}
|
void ClusteringProcessor::StopProcessor() {}
|
||||||
|
|
||||||
|
void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
|
||||||
|
if (type != Core::HID::ControllerTriggerType::IrSensor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_state = {};
|
||||||
|
const auto camera_data = npad_device->GetCamera();
|
||||||
|
auto filtered_image = camera_data.data;
|
||||||
|
|
||||||
|
RemoveLowIntensityData(filtered_image);
|
||||||
|
|
||||||
|
const std::size_t window_start_x =
|
||||||
|
static_cast<std::size_t>(current_config.window_of_interest.x);
|
||||||
|
const std::size_t window_start_y =
|
||||||
|
static_cast<std::size_t>(current_config.window_of_interest.y);
|
||||||
|
const std::size_t window_end_x =
|
||||||
|
window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
|
||||||
|
const std::size_t window_end_y =
|
||||||
|
window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
|
||||||
|
|
||||||
|
for (std::size_t y = window_start_y; y < window_end_y; y++) {
|
||||||
|
for (std::size_t x = window_start_x; x < window_end_x; x++) {
|
||||||
|
u8 pixel = GetPixel(filtered_image, x, y);
|
||||||
|
if (pixel == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto cluster = GetClusterProperties(filtered_image, x, y);
|
||||||
|
if (cluster.pixel_count > current_config.pixel_count_max) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cluster.pixel_count < current_config.pixel_count_min) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Cluster object limit reached
|
||||||
|
if (next_state.object_count >= 0x10) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
next_state.data[next_state.object_count] = cluster;
|
||||||
|
next_state.object_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_state.sampling_number = camera_data.sample;
|
||||||
|
next_state.timestamp = next_state.timestamp + 131;
|
||||||
|
next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
|
||||||
|
shared_memory->clustering_lifo.WriteNextEntry(next_state);
|
||||||
|
|
||||||
|
if (!IsProcessorActive()) {
|
||||||
|
StartProcessor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
|
||||||
|
for (u8& pixel : data) {
|
||||||
|
if (pixel < current_config.pixel_count_min) {
|
||||||
|
pixel = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
|
||||||
|
std::size_t x,
|
||||||
|
std::size_t y) {
|
||||||
|
std::queue<Common::Point<std::size_t>> search_points{};
|
||||||
|
ClusteringData current_cluster = GetPixelProperties(data, x, y);
|
||||||
|
SetPixel(data, x, y, 0);
|
||||||
|
search_points.push({x, y});
|
||||||
|
|
||||||
|
while (!search_points.empty()) {
|
||||||
|
const auto point = search_points.front();
|
||||||
|
search_points.pop();
|
||||||
|
|
||||||
|
// Avoid negative numbers
|
||||||
|
if (point.x == 0 || point.y == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<Common::Point<std::size_t>, 4> new_points{
|
||||||
|
Common::Point<std::size_t>{point.x - 1, point.y},
|
||||||
|
{point.x, point.y - 1},
|
||||||
|
{point.x + 1, point.y},
|
||||||
|
{point.x, point.y + 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto new_point : new_points) {
|
||||||
|
if (new_point.x < 0 || new_point.x >= width) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (new_point.y < 0 || new_point.y >= height) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
|
||||||
|
current_cluster = MergeCluster(current_cluster, cluster);
|
||||||
|
SetPixel(data, new_point.x, new_point.y, 0);
|
||||||
|
search_points.push({new_point.x, new_point.y});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_cluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
|
||||||
|
const std::vector<u8>& data, std::size_t x, std::size_t y) const {
|
||||||
|
return {
|
||||||
|
.average_intensity = GetPixel(data, x, y) / 255.0f,
|
||||||
|
.centroid =
|
||||||
|
{
|
||||||
|
.x = static_cast<f32>(x),
|
||||||
|
.y = static_cast<f32>(y),
|
||||||
|
|
||||||
|
},
|
||||||
|
.pixel_count = 1,
|
||||||
|
.bound =
|
||||||
|
{
|
||||||
|
.x = static_cast<s16>(x),
|
||||||
|
.y = static_cast<s16>(y),
|
||||||
|
.width = 1,
|
||||||
|
.height = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
|
||||||
|
const ClusteringData a, const ClusteringData b) const {
|
||||||
|
const u32 pixel_count = a.pixel_count + b.pixel_count;
|
||||||
|
const f32 average_intensitiy =
|
||||||
|
(a.average_intensity * a.pixel_count + b.average_intensity * b.pixel_count) / pixel_count;
|
||||||
|
const Core::IrSensor::IrsCentroid centroid = {
|
||||||
|
.x = (a.centroid.x * a.pixel_count + b.centroid.x * b.pixel_count) / pixel_count,
|
||||||
|
.y = (a.centroid.y * a.pixel_count + b.centroid.y * b.pixel_count) / pixel_count,
|
||||||
|
};
|
||||||
|
s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
|
||||||
|
s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
|
||||||
|
s16 a_bound_end_x = a.bound.x + a.bound.width;
|
||||||
|
s16 a_bound_end_y = a.bound.y + a.bound.height;
|
||||||
|
s16 b_bound_end_x = b.bound.x + b.bound.width;
|
||||||
|
s16 b_bound_end_y = b.bound.y + b.bound.height;
|
||||||
|
|
||||||
|
const Core::IrSensor::IrsRect bound = {
|
||||||
|
.x = bound_start_x,
|
||||||
|
.y = bound_start_y,
|
||||||
|
.width = a_bound_end_x > b_bound_end_x ? a_bound_end_x - bound_start_x
|
||||||
|
: b_bound_end_x - bound_start_x,
|
||||||
|
.height = a_bound_end_y > b_bound_end_y ? a_bound_end_y - bound_start_y
|
||||||
|
: b_bound_end_y - bound_start_y,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
.average_intensity = average_intensitiy,
|
||||||
|
.centroid = centroid,
|
||||||
|
.pixel_count = pixel_count,
|
||||||
|
.bound = bound,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
|
||||||
|
if ((y * width) + x > data.size()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return data[(y * width) + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
|
||||||
|
if ((y * width) + x > data.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[(y * width) + x] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClusteringProcessor::SetDefaultConfig() {
|
||||||
|
current_config.camera_config.exposure_time = 200000;
|
||||||
|
current_config.camera_config.gain = 2;
|
||||||
|
current_config.camera_config.is_negative_used = false;
|
||||||
|
current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
|
||||||
|
current_config.window_of_interest = {
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
current_config.pixel_count_min = 3;
|
||||||
|
current_config.pixel_count_max = 0x12C00;
|
||||||
|
current_config.is_external_light_filter_enabled = true;
|
||||||
|
current_config.object_intensity_min = 150;
|
||||||
|
|
||||||
|
npad_device->SetCameraFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
|
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
|
||||||
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
|
||||||
current_config.camera_config.gain = config.camera_config.gain;
|
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.is_negative_used = config.camera_config.is_negative_used;
|
||||||
current_config.camera_config.light_target =
|
current_config.camera_config.light_target =
|
||||||
static_cast<Core::IrSensor::CameraLightTarget>(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.pixel_count_min = config.pixel_count_min;
|
current_config.pixel_count_min = config.pixel_count_min;
|
||||||
current_config.pixel_count_max = config.pixel_count_max;
|
current_config.pixel_count_max = config.pixel_count_max;
|
||||||
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
|
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
|
||||||
current_config.object_intensity_min = config.object_intensity_min;
|
current_config.object_intensity_min = config.object_intensity_min;
|
||||||
|
|
||||||
|
LOG_INFO(Service_IRS,
|
||||||
|
"Processor config, exposure_time={}, gain={}, is_negative_used={}, "
|
||||||
|
"light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
|
||||||
|
"pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
|
||||||
|
current_config.camera_config.exposure_time, current_config.camera_config.gain,
|
||||||
|
current_config.camera_config.is_negative_used,
|
||||||
|
current_config.camera_config.light_target, current_config.window_of_interest.x,
|
||||||
|
current_config.window_of_interest.y, current_config.window_of_interest.width,
|
||||||
|
current_config.window_of_interest.height, current_config.pixel_count_min,
|
||||||
|
current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
|
||||||
|
current_config.object_intensity_min);
|
||||||
|
|
||||||
|
npad_device->SetCameraFormat(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::IRS
|
} // namespace Service::IRS
|
||||||
|
@ -5,12 +5,19 @@
|
|||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hid/irs_types.h"
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irs_ring_lifo.h"
|
||||||
#include "core/hle/service/hid/irsensor/processor_base.h"
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Core::HID {
|
||||||
|
class EmulatedController;
|
||||||
|
} // namespace Core::HID
|
||||||
|
|
||||||
namespace Service::IRS {
|
namespace Service::IRS {
|
||||||
class ClusteringProcessor final : public ProcessorBase {
|
class ClusteringProcessor final : public ProcessorBase {
|
||||||
public:
|
public:
|
||||||
explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format);
|
explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
|
||||||
|
Core::IrSensor::DeviceFormat& device_format,
|
||||||
|
std::size_t npad_index);
|
||||||
~ClusteringProcessor() override;
|
~ClusteringProcessor() override;
|
||||||
|
|
||||||
// Called when the processor is initialized
|
// Called when the processor is initialized
|
||||||
@ -26,6 +33,10 @@ public:
|
|||||||
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
|
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
|
||||||
|
static constexpr std::size_t width = 320;
|
||||||
|
static constexpr std::size_t height = 240;
|
||||||
|
|
||||||
// This is nn::irsensor::ClusteringProcessorConfig
|
// This is nn::irsensor::ClusteringProcessorConfig
|
||||||
struct ClusteringProcessorConfig {
|
struct ClusteringProcessorConfig {
|
||||||
Core::IrSensor::CameraConfig camera_config;
|
Core::IrSensor::CameraConfig camera_config;
|
||||||
@ -68,7 +79,32 @@ private:
|
|||||||
static_assert(sizeof(ClusteringProcessorState) == 0x198,
|
static_assert(sizeof(ClusteringProcessorState) == 0x198,
|
||||||
"ClusteringProcessorState is an invalid size");
|
"ClusteringProcessorState is an invalid size");
|
||||||
|
|
||||||
|
struct ClusteringSharedMemory {
|
||||||
|
Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
|
||||||
|
static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
|
||||||
|
INSERT_PADDING_WORDS(0x11F);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
|
||||||
|
"ClusteringSharedMemory is an invalid size");
|
||||||
|
|
||||||
|
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
|
||||||
|
void RemoveLowIntensityData(std::vector<u8>& data);
|
||||||
|
ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
|
||||||
|
ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
|
||||||
|
std::size_t y) const;
|
||||||
|
ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
|
||||||
|
u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
|
||||||
|
void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
|
||||||
|
|
||||||
|
// Sets config parameters of the camera
|
||||||
|
void SetDefaultConfig();
|
||||||
|
|
||||||
|
ClusteringSharedMemory* shared_memory = nullptr;
|
||||||
|
ClusteringProcessorState next_state{};
|
||||||
|
|
||||||
ClusteringProcessorConfig current_config{};
|
ClusteringProcessorConfig current_config{};
|
||||||
Core::IrSensor::DeviceFormat& device;
|
Core::IrSensor::DeviceFormat& device;
|
||||||
|
Core::HID::EmulatedController* npad_device;
|
||||||
|
int callback_key{};
|
||||||
};
|
};
|
||||||
} // namespace Service::IRS
|
} // namespace Service::IRS
|
||||||
|
@ -838,7 +838,7 @@ void GRenderWindow::InitializeCamera() {
|
|||||||
camera_timer = std::make_unique<QTimer>();
|
camera_timer = std::make_unique<QTimer>();
|
||||||
connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
|
connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
|
||||||
// This timer should be dependent of camera resolution 5ms for every 100 pixels
|
// This timer should be dependent of camera resolution 5ms for every 100 pixels
|
||||||
camera_timer->start(100);
|
camera_timer->start(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::FinalizeCamera() {
|
void GRenderWindow::FinalizeCamera() {
|
||||||
|
Loading…
Reference in New Issue
Block a user