early-access version 2153
This commit is contained in:
parent
fe662edf4a
commit
238cebb24e
@ -411,12 +411,13 @@ if (CONAN_REQUIRED_LIBS)
|
||||
# Download conan.cmake automatically, you can also just copy the conan.cmake file
|
||||
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
|
||||
file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/v0.15/conan.cmake"
|
||||
# TODO: Use a tagged release. The latest tagged release does not support VS2022 as of this writing.
|
||||
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/43e385830ee35377dbd2dcbe8d5a9e750301ea00/conan.cmake"
|
||||
"${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
endif()
|
||||
include(${CMAKE_BINARY_DIR}/conan.cmake)
|
||||
|
||||
conan_check(VERSION 1.24.0 REQUIRED)
|
||||
conan_check(VERSION 1.41.0 REQUIRED)
|
||||
|
||||
# Manually add iconv to fix a dep conflict between qt and sdl2
|
||||
# We don't need to add it through find_package or anything since the other two can find it just fine
|
||||
|
@ -1,7 +1,7 @@
|
||||
yuzu emulator early access
|
||||
=============
|
||||
|
||||
This is the source code for early-access 2149.
|
||||
This is the source code for early-access 2153.
|
||||
|
||||
## Legal Notice
|
||||
|
||||
|
@ -64,7 +64,7 @@ public:
|
||||
using propagate_on_container_copy_assignment = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using propagate_on_container_swap = std::true_type;
|
||||
using is_always_equal = std::true_type;
|
||||
using is_always_equal = std::false_type;
|
||||
|
||||
constexpr AlignmentAllocator() noexcept = default;
|
||||
|
||||
@ -83,6 +83,11 @@ public:
|
||||
struct rebind {
|
||||
using other = AlignmentAllocator<T2, Align>;
|
||||
};
|
||||
|
||||
template <typename T2, size_t Align2>
|
||||
constexpr bool operator==(const AlignmentAllocator<T2, Align2>&) const noexcept {
|
||||
return std::is_same_v<T, T2> && Align == Align2;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
240
src/common/atomic_threadsafe_queue.h
Executable file
240
src/common/atomic_threadsafe_queue.h
Executable file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324)
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstddef> // offsetof
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Common {
|
||||
namespace mpmc {
|
||||
static constexpr size_t hardwareInterferenceSize = 64;
|
||||
|
||||
template <typename T>
|
||||
using AlignedAllocator = std::allocator<T>;
|
||||
|
||||
template <typename T>
|
||||
struct Slot {
|
||||
~Slot() noexcept {
|
||||
if (turn & 1) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible<T, Args&&...>::value,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
new (&storage) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void destroy() noexcept {
|
||||
static_assert(std::is_nothrow_destructible<T>::value, "T must be nothrow destructible");
|
||||
reinterpret_cast<T*>(&storage)->~T();
|
||||
}
|
||||
|
||||
T&& move() noexcept {
|
||||
return reinterpret_cast<T&&>(storage);
|
||||
}
|
||||
|
||||
// Align to avoid false sharing between adjacent slots
|
||||
alignas(hardwareInterferenceSize) std::atomic<size_t> turn = {0};
|
||||
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage;
|
||||
};
|
||||
|
||||
template <typename T, typename Allocator = AlignedAllocator<Slot<T>>>
|
||||
class Queue {
|
||||
private:
|
||||
static_assert(std::is_nothrow_copy_assignable<T>::value ||
|
||||
std::is_nothrow_move_assignable<T>::value,
|
||||
"T must be nothrow copy or move assignable");
|
||||
|
||||
static_assert(std::is_nothrow_destructible<T>::value, "T must be nothrow destructible");
|
||||
|
||||
public:
|
||||
explicit Queue(const size_t capacity, const Allocator& allocator = Allocator())
|
||||
: capacity_(capacity), allocator_(allocator), head_(0), tail_(0) {
|
||||
if (capacity_ < 1) {
|
||||
throw std::invalid_argument("capacity < 1");
|
||||
}
|
||||
// Allocate one extra slot to prevent false sharing on the last slot
|
||||
slots_ = allocator_.allocate(capacity_ + 1);
|
||||
// Allocators are not required to honor alignment for over-aligned types
|
||||
// (see http://eel.is/c++draft/allocator.requirements#10) so we verify
|
||||
// alignment here
|
||||
if (reinterpret_cast<size_t>(slots_) % alignof(Slot<T>) != 0) {
|
||||
allocator_.deallocate(slots_, capacity_ + 1);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
for (size_t i = 0; i < capacity_; ++i) {
|
||||
new (&slots_[i]) Slot<T>();
|
||||
}
|
||||
static_assert(alignof(Slot<T>) == hardwareInterferenceSize,
|
||||
"Slot must be aligned to cache line boundary to prevent false sharing");
|
||||
static_assert(sizeof(Slot<T>) % hardwareInterferenceSize == 0,
|
||||
"Slot size must be a multiple of cache line size to prevent "
|
||||
"false sharing between adjacent slots");
|
||||
static_assert(sizeof(Queue) % hardwareInterferenceSize == 0,
|
||||
"Queue size must be a multiple of cache line size to "
|
||||
"prevent false sharing between adjacent queues");
|
||||
static_assert(offsetof(Queue, tail_) - offsetof(Queue, head_) ==
|
||||
static_cast<std::ptrdiff_t>(hardwareInterferenceSize),
|
||||
"head and tail must be a cache line apart to prevent false sharing");
|
||||
}
|
||||
|
||||
~Queue() noexcept {
|
||||
for (size_t i = 0; i < capacity_; ++i) {
|
||||
slots_[i].~Slot();
|
||||
}
|
||||
allocator_.deallocate(slots_, capacity_ + 1);
|
||||
}
|
||||
|
||||
// non-copyable and non-movable
|
||||
Queue(const Queue&) = delete;
|
||||
Queue& operator=(const Queue&) = delete;
|
||||
|
||||
template <typename... Args>
|
||||
void emplace(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible<T, Args&&...>::value,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
auto const head = head_.fetch_add(1);
|
||||
auto& slot = slots_[idx(head)];
|
||||
while (turn(head) * 2 != slot.turn.load(std::memory_order_acquire))
|
||||
;
|
||||
slot.construct(std::forward<Args>(args)...);
|
||||
slot.turn.store(turn(head) * 2 + 1, std::memory_order_release);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool try_emplace(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible<T, Args&&...>::value,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
auto head = head_.load(std::memory_order_acquire);
|
||||
for (;;) {
|
||||
auto& slot = slots_[idx(head)];
|
||||
if (turn(head) * 2 == slot.turn.load(std::memory_order_acquire)) {
|
||||
if (head_.compare_exchange_strong(head, head + 1)) {
|
||||
slot.construct(std::forward<Args>(args)...);
|
||||
slot.turn.store(turn(head) * 2 + 1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
auto const prevHead = head;
|
||||
head = head_.load(std::memory_order_acquire);
|
||||
if (head == prevHead) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void push(const T& v) noexcept {
|
||||
static_assert(std::is_nothrow_copy_constructible<T>::value,
|
||||
"T must be nothrow copy constructible");
|
||||
emplace(v);
|
||||
}
|
||||
|
||||
template <typename P, typename = typename std::enable_if<
|
||||
std::is_nothrow_constructible<T, P&&>::value>::type>
|
||||
void push(P&& v) noexcept {
|
||||
emplace(std::forward<P>(v));
|
||||
}
|
||||
|
||||
bool try_push(const T& v) noexcept {
|
||||
static_assert(std::is_nothrow_copy_constructible<T>::value,
|
||||
"T must be nothrow copy constructible");
|
||||
return try_emplace(v);
|
||||
}
|
||||
|
||||
template <typename P, typename = typename std::enable_if<
|
||||
std::is_nothrow_constructible<T, P&&>::value>::type>
|
||||
bool try_push(P&& v) noexcept {
|
||||
return try_emplace(std::forward<P>(v));
|
||||
}
|
||||
|
||||
void pop(T& v) noexcept {
|
||||
auto const tail = tail_.fetch_add(1);
|
||||
auto& slot = slots_[idx(tail)];
|
||||
while (turn(tail) * 2 + 1 != slot.turn.load(std::memory_order_acquire))
|
||||
;
|
||||
v = slot.move();
|
||||
slot.destroy();
|
||||
slot.turn.store(turn(tail) * 2 + 2, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool try_pop(T& v) noexcept {
|
||||
auto tail = tail_.load(std::memory_order_acquire);
|
||||
for (;;) {
|
||||
auto& slot = slots_[idx(tail)];
|
||||
if (turn(tail) * 2 + 1 == slot.turn.load(std::memory_order_acquire)) {
|
||||
if (tail_.compare_exchange_strong(tail, tail + 1)) {
|
||||
v = slot.move();
|
||||
slot.destroy();
|
||||
slot.turn.store(turn(tail) * 2 + 2, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
auto const prevTail = tail;
|
||||
tail = tail_.load(std::memory_order_acquire);
|
||||
if (tail == prevTail) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr size_t idx(size_t i) const noexcept {
|
||||
return i % capacity_;
|
||||
}
|
||||
|
||||
constexpr size_t turn(size_t i) const noexcept {
|
||||
return i / capacity_;
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t capacity_;
|
||||
Slot<T>* slots_;
|
||||
[[no_unique_address]] Allocator allocator_;
|
||||
|
||||
// Align to avoid false sharing between head_ and tail_
|
||||
alignas(hardwareInterferenceSize) std::atomic<size_t> head_;
|
||||
alignas(hardwareInterferenceSize) std::atomic<size_t> tail_;
|
||||
};
|
||||
} // namespace mpmc
|
||||
|
||||
template <typename T, typename Allocator = mpmc::AlignedAllocator<mpmc::Slot<T>>>
|
||||
using MPMCQueue = mpmc::Queue<T, Allocator>;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
@ -69,8 +69,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
std::vector<Reloc> relocs(params.relocation_count);
|
||||
std::vector<u32> reloc_shifts(params.relocation_count);
|
||||
std::vector<SyncptIncr> syncpt_increments(params.syncpoint_count);
|
||||
std::vector<SyncptIncr> wait_checks(params.syncpoint_count);
|
||||
std::vector<Fence> fences(params.fence_count);
|
||||
std::vector<u32> fence_thresholds(params.fence_count);
|
||||
|
||||
// Slice input into their respective buffers
|
||||
std::size_t offset = sizeof(IoctlSubmit);
|
||||
@ -78,15 +77,13 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
offset += SliceVectors(input, relocs, params.relocation_count, offset);
|
||||
offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset);
|
||||
offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
|
||||
offset += SliceVectors(input, wait_checks, params.syncpoint_count, offset);
|
||||
offset += SliceVectors(input, fences, params.fence_count, offset);
|
||||
offset += SliceVectors(input, fence_thresholds, params.fence_count, offset);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
if (gpu.UseNvdec()) {
|
||||
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
|
||||
const SyncptIncr& syncpt_incr = syncpt_increments[i];
|
||||
fences[i].id = syncpt_incr.id;
|
||||
fences[i].value =
|
||||
fence_thresholds[i] =
|
||||
syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments);
|
||||
}
|
||||
}
|
||||
@ -98,11 +95,6 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
cmdlist.size() * sizeof(u32));
|
||||
gpu.PushCommandBuffer(cmdlist);
|
||||
}
|
||||
if (gpu.UseNvdec()) {
|
||||
fences[0].value = syncpoint_manager.IncreaseSyncpoint(fences[0].id, 1);
|
||||
Tegra::ChCommandHeaderList cmdlist{{(4 << 28) | fences[0].id}};
|
||||
gpu.PushCommandBuffer(cmdlist);
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit));
|
||||
// Some games expect command_buffers to be written back
|
||||
offset = sizeof(IoctlSubmit);
|
||||
@ -110,8 +102,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
offset += WriteVectors(output, relocs, offset);
|
||||
offset += WriteVectors(output, reloc_shifts, offset);
|
||||
offset += WriteVectors(output, syncpt_increments, offset);
|
||||
offset += WriteVectors(output, wait_checks, offset);
|
||||
offset += WriteVectors(output, fences, offset);
|
||||
offset += WriteVectors(output, fence_thresholds, offset);
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
@ -56,19 +56,16 @@ protected:
|
||||
s32 target{};
|
||||
s32 target_offset{};
|
||||
};
|
||||
static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
|
||||
static_assert(sizeof(Reloc) == 0x10, "Reloc has incorrect size");
|
||||
|
||||
struct SyncptIncr {
|
||||
u32 id{};
|
||||
u32 increments{};
|
||||
u32 unk0{};
|
||||
u32 unk1{};
|
||||
u32 unk2{};
|
||||
};
|
||||
static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
|
||||
|
||||
struct Fence {
|
||||
u32 id{};
|
||||
u32 value{};
|
||||
};
|
||||
static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
|
||||
static_assert(sizeof(SyncptIncr) == 0x14, "SyncptIncr has incorrect size");
|
||||
|
||||
struct IoctlGetSyncpoint {
|
||||
// Input
|
||||
|
@ -14,73 +14,15 @@
|
||||
|
||||
namespace GCAdapter {
|
||||
|
||||
class LibUSBContext {
|
||||
public:
|
||||
explicit LibUSBContext() {
|
||||
init_result = libusb_init(&ctx);
|
||||
}
|
||||
|
||||
~LibUSBContext() {
|
||||
libusb_exit(ctx);
|
||||
}
|
||||
|
||||
LibUSBContext& operator=(const LibUSBContext&) = delete;
|
||||
LibUSBContext(const LibUSBContext&) = delete;
|
||||
|
||||
LibUSBContext& operator=(LibUSBContext&&) noexcept = delete;
|
||||
LibUSBContext(LibUSBContext&&) noexcept = delete;
|
||||
|
||||
[[nodiscard]] int InitResult() const noexcept {
|
||||
return init_result;
|
||||
}
|
||||
|
||||
[[nodiscard]] libusb_context* get() noexcept {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
private:
|
||||
libusb_context* ctx;
|
||||
int init_result{};
|
||||
};
|
||||
|
||||
class LibUSBDeviceHandle {
|
||||
public:
|
||||
explicit LibUSBDeviceHandle(libusb_context* ctx, uint16_t vid, uint16_t pid) noexcept {
|
||||
handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
|
||||
}
|
||||
|
||||
~LibUSBDeviceHandle() noexcept {
|
||||
if (handle) {
|
||||
libusb_release_interface(handle, 1);
|
||||
libusb_close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
LibUSBDeviceHandle& operator=(const LibUSBDeviceHandle&) = delete;
|
||||
LibUSBDeviceHandle(const LibUSBDeviceHandle&) = delete;
|
||||
|
||||
LibUSBDeviceHandle& operator=(LibUSBDeviceHandle&&) noexcept = delete;
|
||||
LibUSBDeviceHandle(LibUSBDeviceHandle&&) noexcept = delete;
|
||||
|
||||
[[nodiscard]] libusb_device_handle* get() noexcept {
|
||||
return handle;
|
||||
}
|
||||
|
||||
private:
|
||||
libusb_device_handle* handle{};
|
||||
};
|
||||
|
||||
Adapter::Adapter() {
|
||||
if (usb_adapter_handle) {
|
||||
if (usb_adapter_handle != nullptr) {
|
||||
return;
|
||||
}
|
||||
LOG_INFO(Input, "GC Adapter Initialization started");
|
||||
|
||||
libusb_ctx = std::make_unique<LibUSBContext>();
|
||||
const int init_res = libusb_ctx->InitResult();
|
||||
const int init_res = libusb_init(&libusb_ctx);
|
||||
if (init_res == LIBUSB_SUCCESS) {
|
||||
adapter_scan_thread =
|
||||
std::jthread([this](std::stop_token stop_token) { AdapterScanThread(stop_token); });
|
||||
adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
|
||||
} else {
|
||||
LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
|
||||
}
|
||||
@ -90,15 +32,17 @@ Adapter::~Adapter() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Adapter::AdapterInputThread(std::stop_token stop_token) {
|
||||
void Adapter::AdapterInputThread() {
|
||||
LOG_DEBUG(Input, "GC Adapter input thread started");
|
||||
s32 payload_size{};
|
||||
AdapterPayload adapter_payload{};
|
||||
|
||||
adapter_scan_thread = {};
|
||||
if (adapter_scan_thread.joinable()) {
|
||||
adapter_scan_thread.join();
|
||||
}
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
libusb_interrupt_transfer(usb_adapter_handle->get(), input_endpoint, adapter_payload.data(),
|
||||
while (adapter_input_thread_running) {
|
||||
libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
|
||||
static_cast<s32>(adapter_payload.size()), &payload_size, 16);
|
||||
if (IsPayloadCorrect(adapter_payload, payload_size)) {
|
||||
UpdateControllers(adapter_payload);
|
||||
@ -108,8 +52,7 @@ void Adapter::AdapterInputThread(std::stop_token stop_token) {
|
||||
}
|
||||
|
||||
if (restart_scan_thread) {
|
||||
adapter_scan_thread =
|
||||
std::jthread([this](std::stop_token token) { AdapterScanThread(token); });
|
||||
adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
|
||||
restart_scan_thread = false;
|
||||
}
|
||||
}
|
||||
@ -121,7 +64,7 @@ bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payloa
|
||||
adapter_payload[0]);
|
||||
if (input_error_counter++ > 20) {
|
||||
LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?");
|
||||
adapter_input_thread.request_stop();
|
||||
adapter_input_thread_running = false;
|
||||
restart_scan_thread = true;
|
||||
}
|
||||
return false;
|
||||
@ -153,7 +96,7 @@ void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
|
||||
return;
|
||||
}
|
||||
// Device changed reset device and set new type
|
||||
pads[port] = {};
|
||||
ResetDevice(port);
|
||||
pads[port].type = pad_type;
|
||||
}
|
||||
|
||||
@ -227,7 +170,7 @@ void Adapter::UpdateYuzuSettings(std::size_t port) {
|
||||
|
||||
if (pads[port].buttons != 0) {
|
||||
pad_status.button = pads[port].last_button;
|
||||
pad_queue.Push(pad_status);
|
||||
pad_queue.push(pad_status);
|
||||
}
|
||||
|
||||
// Accounting for a threshold here to ensure an intentional press
|
||||
@ -238,7 +181,7 @@ void Adapter::UpdateYuzuSettings(std::size_t port) {
|
||||
pad_status.axis = static_cast<PadAxes>(i);
|
||||
pad_status.axis_value = value;
|
||||
pad_status.axis_threshold = axis_threshold;
|
||||
pad_queue.Push(pad_status);
|
||||
pad_queue.push(pad_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -270,9 +213,8 @@ void Adapter::SendVibrations() {
|
||||
const u8 p3 = pads[2].enable_vibration;
|
||||
const u8 p4 = pads[3].enable_vibration;
|
||||
std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
|
||||
const int err =
|
||||
libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, payload.data(),
|
||||
static_cast<s32>(payload.size()), &size, 16);
|
||||
const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
|
||||
static_cast<s32>(payload.size()), &size, 16);
|
||||
if (err) {
|
||||
LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
|
||||
if (output_error_counter++ > 5) {
|
||||
@ -291,53 +233,56 @@ bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
|
||||
return rumble_enabled;
|
||||
}
|
||||
|
||||
void Adapter::AdapterScanThread(std::stop_token stop_token) {
|
||||
usb_adapter_handle = nullptr;
|
||||
pads = {};
|
||||
while (!stop_token.stop_requested() && !Setup()) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
void Adapter::AdapterScanThread() {
|
||||
adapter_scan_thread_running = true;
|
||||
adapter_input_thread_running = false;
|
||||
if (adapter_input_thread.joinable()) {
|
||||
adapter_input_thread.join();
|
||||
}
|
||||
ClearLibusbHandle();
|
||||
ResetDevices();
|
||||
while (adapter_scan_thread_running && !adapter_input_thread_running) {
|
||||
Setup();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
bool Adapter::Setup() {
|
||||
constexpr u16 nintendo_vid = 0x057e;
|
||||
constexpr u16 gc_adapter_pid = 0x0337;
|
||||
usb_adapter_handle =
|
||||
std::make_unique<LibUSBDeviceHandle>(libusb_ctx->get(), nintendo_vid, gc_adapter_pid);
|
||||
if (!usb_adapter_handle->get()) {
|
||||
return false;
|
||||
void Adapter::Setup() {
|
||||
usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337);
|
||||
|
||||
if (usb_adapter_handle == NULL) {
|
||||
return;
|
||||
}
|
||||
if (!CheckDeviceAccess()) {
|
||||
usb_adapter_handle = nullptr;
|
||||
return false;
|
||||
ClearLibusbHandle();
|
||||
return;
|
||||
}
|
||||
|
||||
libusb_device* const device = libusb_get_device(usb_adapter_handle->get());
|
||||
libusb_device* device = libusb_get_device(usb_adapter_handle);
|
||||
|
||||
LOG_INFO(Input, "GC adapter is now connected");
|
||||
// GC Adapter found and accessible, registering it
|
||||
if (GetGCEndpoint(device)) {
|
||||
adapter_scan_thread_running = false;
|
||||
adapter_input_thread_running = true;
|
||||
rumble_enabled = true;
|
||||
input_error_counter = 0;
|
||||
output_error_counter = 0;
|
||||
adapter_input_thread =
|
||||
std::jthread([this](std::stop_token stop_token) { AdapterInputThread(stop_token); });
|
||||
return true;
|
||||
adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Adapter::CheckDeviceAccess() {
|
||||
// This fixes payload problems from offbrand GCAdapters
|
||||
const s32 control_transfer_error =
|
||||
libusb_control_transfer(usb_adapter_handle->get(), 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
|
||||
libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
|
||||
if (control_transfer_error < 0) {
|
||||
LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
|
||||
}
|
||||
|
||||
s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle->get(), 0);
|
||||
s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
|
||||
if (kernel_driver_error == 1) {
|
||||
kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle->get(), 0);
|
||||
kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
|
||||
if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
|
||||
LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
|
||||
kernel_driver_error);
|
||||
@ -345,13 +290,15 @@ bool Adapter::CheckDeviceAccess() {
|
||||
}
|
||||
|
||||
if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
|
||||
libusb_close(usb_adapter_handle);
|
||||
usb_adapter_handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const int interface_claim_error = libusb_claim_interface(usb_adapter_handle->get(), 0);
|
||||
const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0);
|
||||
if (interface_claim_error) {
|
||||
LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
|
||||
libusb_close(usb_adapter_handle);
|
||||
usb_adapter_handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
@ -385,17 +332,57 @@ bool Adapter::GetGCEndpoint(libusb_device* device) {
|
||||
// This transfer seems to be responsible for clearing the state of the adapter
|
||||
// Used to clear the "busy" state of when the device is unexpectedly unplugged
|
||||
unsigned char clear_payload = 0x13;
|
||||
libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, &clear_payload,
|
||||
libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
|
||||
sizeof(clear_payload), nullptr, 16);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Adapter::JoinThreads() {
|
||||
restart_scan_thread = false;
|
||||
adapter_input_thread_running = false;
|
||||
adapter_scan_thread_running = false;
|
||||
|
||||
if (adapter_scan_thread.joinable()) {
|
||||
adapter_scan_thread.join();
|
||||
}
|
||||
|
||||
if (adapter_input_thread.joinable()) {
|
||||
adapter_input_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Adapter::ClearLibusbHandle() {
|
||||
if (usb_adapter_handle) {
|
||||
libusb_release_interface(usb_adapter_handle, 1);
|
||||
libusb_close(usb_adapter_handle);
|
||||
usb_adapter_handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Adapter::ResetDevices() {
|
||||
for (std::size_t i = 0; i < pads.size(); ++i) {
|
||||
ResetDevice(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Adapter::ResetDevice(std::size_t port) {
|
||||
pads[port].type = ControllerTypes::None;
|
||||
pads[port].enable_vibration = false;
|
||||
pads[port].rumble_amplitude = 0;
|
||||
pads[port].buttons = 0;
|
||||
pads[port].last_button = PadButton::Undefined;
|
||||
pads[port].axis_values.fill(0);
|
||||
pads[port].reset_origin_counter = 0;
|
||||
}
|
||||
|
||||
void Adapter::Reset() {
|
||||
adapter_scan_thread = {};
|
||||
adapter_input_thread = {};
|
||||
usb_adapter_handle = nullptr;
|
||||
pads = {};
|
||||
libusb_ctx = nullptr;
|
||||
JoinThreads();
|
||||
ClearLibusbHandle();
|
||||
ResetDevices();
|
||||
|
||||
if (libusb_ctx) {
|
||||
libusb_exit(libusb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
|
||||
@ -491,20 +478,18 @@ bool Adapter::DeviceConnected(std::size_t port) const {
|
||||
}
|
||||
|
||||
void Adapter::BeginConfiguration() {
|
||||
pad_queue.Clear();
|
||||
configuring = true;
|
||||
}
|
||||
|
||||
void Adapter::EndConfiguration() {
|
||||
pad_queue.Clear();
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() {
|
||||
Common::MPMCQueue<GCPadStatus>& Adapter::GetPadQueue() {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
|
||||
const Common::MPMCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
|
@ -3,16 +3,13 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/atomic_threadsafe_queue.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
struct libusb_context;
|
||||
@ -21,9 +18,6 @@ struct libusb_device_handle;
|
||||
|
||||
namespace GCAdapter {
|
||||
|
||||
class LibUSBContext;
|
||||
class LibUSBDeviceHandle;
|
||||
|
||||
enum class PadButton {
|
||||
Undefined = 0x0000,
|
||||
ButtonLeft = 0x0001,
|
||||
@ -69,11 +63,11 @@ struct GCPadStatus {
|
||||
};
|
||||
|
||||
struct GCController {
|
||||
ControllerTypes type = ControllerTypes::None;
|
||||
bool enable_vibration = false;
|
||||
u8 rumble_amplitude = 0;
|
||||
u16 buttons = 0;
|
||||
PadButton last_button = PadButton::Undefined;
|
||||
ControllerTypes type{};
|
||||
bool enable_vibration{};
|
||||
u8 rumble_amplitude{};
|
||||
u16 buttons{};
|
||||
PadButton last_button{};
|
||||
std::array<s16, 6> axis_values{};
|
||||
std::array<u8, 6> axis_origin{};
|
||||
u8 reset_origin_counter{};
|
||||
@ -91,8 +85,8 @@ public:
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
Common::SPSCQueue<GCPadStatus>& GetPadQueue();
|
||||
const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
|
||||
Common::MPMCQueue<GCPadStatus>& GetPadQueue();
|
||||
const Common::MPMCQueue<GCPadStatus>& GetPadQueue() const;
|
||||
|
||||
GCController& GetPadState(std::size_t port);
|
||||
const GCController& GetPadState(std::size_t port) const;
|
||||
@ -115,9 +109,9 @@ private:
|
||||
void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
|
||||
void UpdateVibrations();
|
||||
|
||||
void AdapterInputThread(std::stop_token stop_token);
|
||||
void AdapterInputThread();
|
||||
|
||||
void AdapterScanThread(std::stop_token stop_token);
|
||||
void AdapterScanThread();
|
||||
|
||||
bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
|
||||
|
||||
@ -125,7 +119,13 @@ private:
|
||||
void SendVibrations();
|
||||
|
||||
/// For use in initialization, querying devices to find the adapter
|
||||
bool Setup();
|
||||
void Setup();
|
||||
|
||||
/// Resets status of all GC controller devices to a disconnected state
|
||||
void ResetDevices();
|
||||
|
||||
/// Resets status of device connected to a disconnected state
|
||||
void ResetDevice(std::size_t port);
|
||||
|
||||
/// Returns true if we successfully gain access to GC Adapter
|
||||
bool CheckDeviceAccess();
|
||||
@ -137,15 +137,23 @@ private:
|
||||
/// For shutting down, clear all data, join all threads, release usb
|
||||
void Reset();
|
||||
|
||||
std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle;
|
||||
// Join all threads
|
||||
void JoinThreads();
|
||||
|
||||
// Release usb handles
|
||||
void ClearLibusbHandle();
|
||||
|
||||
libusb_device_handle* usb_adapter_handle = nullptr;
|
||||
std::array<GCController, 4> pads;
|
||||
Common::SPSCQueue<GCPadStatus> pad_queue;
|
||||
Common::MPMCQueue<GCPadStatus> pad_queue{1024};
|
||||
|
||||
std::jthread adapter_input_thread;
|
||||
std::jthread adapter_scan_thread;
|
||||
bool restart_scan_thread{};
|
||||
std::thread adapter_input_thread;
|
||||
std::thread adapter_scan_thread;
|
||||
bool adapter_input_thread_running;
|
||||
bool adapter_scan_thread_running;
|
||||
bool restart_scan_thread;
|
||||
|
||||
std::unique_ptr<LibUSBContext> libusb_ctx;
|
||||
libusb_context* libusb_ctx;
|
||||
|
||||
u8 input_endpoint{0};
|
||||
u8 output_endpoint{0};
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/atomic_threadsafe_queue.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
|
||||
@ -103,7 +103,7 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const {
|
||||
Common::ParamPackage params;
|
||||
GCAdapter::GCPadStatus pad;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
while (queue.Pop(pad)) {
|
||||
while (queue.try_pop(pad)) {
|
||||
// This while loop will break on the earliest detected button
|
||||
params.Set("engine", "gcpad");
|
||||
params.Set("port", static_cast<s32>(pad.port));
|
||||
@ -263,7 +263,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
|
||||
GCAdapter::GCPadStatus pad;
|
||||
Common::ParamPackage params;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
while (queue.Pop(pad)) {
|
||||
while (queue.try_pop(pad)) {
|
||||
if (pad.button != GCAdapter::PadButton::Undefined) {
|
||||
params.Set("engine", "gcpad");
|
||||
params.Set("port", static_cast<s32>(pad.port));
|
||||
|
@ -52,7 +52,7 @@ void Mouse::UpdateYuzuSettings() {
|
||||
return;
|
||||
}
|
||||
|
||||
mouse_queue.Push(MouseStatus{
|
||||
mouse_queue.push(MouseStatus{
|
||||
.button = last_button,
|
||||
});
|
||||
}
|
||||
@ -153,7 +153,6 @@ void Mouse::ReleaseAllButtons() {
|
||||
void Mouse::BeginConfiguration() {
|
||||
buttons = 0;
|
||||
last_button = MouseButton::Undefined;
|
||||
mouse_queue.Clear();
|
||||
configuring = true;
|
||||
}
|
||||
|
||||
@ -165,7 +164,6 @@ void Mouse::EndConfiguration() {
|
||||
info.data.axis = {0, 0};
|
||||
}
|
||||
last_button = MouseButton::Undefined;
|
||||
mouse_queue.Clear();
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
@ -205,11 +203,11 @@ bool Mouse::UnlockButton(std::size_t button_) {
|
||||
return button_state;
|
||||
}
|
||||
|
||||
Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() {
|
||||
Common::MPMCQueue<MouseStatus>& Mouse::GetMouseQueue() {
|
||||
return mouse_queue;
|
||||
}
|
||||
|
||||
const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
|
||||
const Common::MPMCQueue<MouseStatus>& Mouse::GetMouseQueue() const {
|
||||
return mouse_queue;
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
|
||||
#include "common/atomic_threadsafe_queue.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/motion_input.h"
|
||||
@ -79,8 +79,8 @@ public:
|
||||
[[nodiscard]] bool ToggleButton(std::size_t button_);
|
||||
[[nodiscard]] bool UnlockButton(std::size_t button_);
|
||||
|
||||
[[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue();
|
||||
[[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const;
|
||||
[[nodiscard]] Common::MPMCQueue<MouseStatus>& GetMouseQueue();
|
||||
[[nodiscard]] const Common::MPMCQueue<MouseStatus>& GetMouseQueue() const;
|
||||
|
||||
[[nodiscard]] MouseData& GetMouseState(std::size_t button);
|
||||
[[nodiscard]] const MouseData& GetMouseState(std::size_t button) const;
|
||||
@ -109,7 +109,7 @@ private:
|
||||
std::jthread update_thread;
|
||||
MouseButton last_button{MouseButton::Undefined};
|
||||
std::array<MouseInfo, 7> mouse_info;
|
||||
Common::SPSCQueue<MouseStatus> mouse_queue;
|
||||
Common::MPMCQueue<MouseStatus> mouse_queue{1024};
|
||||
bool configuring{false};
|
||||
int mouse_panning_timout{};
|
||||
};
|
||||
|
@ -52,7 +52,7 @@ Common::ParamPackage MouseButtonFactory::GetNextInput() const {
|
||||
MouseInput::MouseStatus pad;
|
||||
Common::ParamPackage params;
|
||||
auto& queue = mouse_input->GetMouseQueue();
|
||||
while (queue.Pop(pad)) {
|
||||
while (queue.try_pop(pad)) {
|
||||
// This while loop will break on the earliest detected button
|
||||
if (pad.button != MouseInput::MouseButton::Undefined) {
|
||||
params.Set("engine", "mouse");
|
||||
@ -184,7 +184,7 @@ Common::ParamPackage MouseAnalogFactory::GetNextInput() const {
|
||||
MouseInput::MouseStatus pad;
|
||||
Common::ParamPackage params;
|
||||
auto& queue = mouse_input->GetMouseQueue();
|
||||
while (queue.Pop(pad)) {
|
||||
while (queue.try_pop(pad)) {
|
||||
// This while loop will break on the earliest detected button
|
||||
if (pad.button != MouseInput::MouseButton::Undefined) {
|
||||
params.Set("engine", "mouse");
|
||||
@ -227,7 +227,7 @@ Common::ParamPackage MouseMotionFactory::GetNextInput() const {
|
||||
MouseInput::MouseStatus pad;
|
||||
Common::ParamPackage params;
|
||||
auto& queue = mouse_input->GetMouseQueue();
|
||||
while (queue.Pop(pad)) {
|
||||
while (queue.try_pop(pad)) {
|
||||
// This while loop will break on the earliest detected button
|
||||
if (pad.button != MouseInput::MouseButton::Undefined) {
|
||||
params.Set("engine", "mouse");
|
||||
@ -275,7 +275,7 @@ Common::ParamPackage MouseTouchFactory::GetNextInput() const {
|
||||
MouseInput::MouseStatus pad;
|
||||
Common::ParamPackage params;
|
||||
auto& queue = mouse_input->GetMouseQueue();
|
||||
while (queue.Pop(pad)) {
|
||||
while (queue.try_pop(pad)) {
|
||||
// This while loop will break on the earliest detected button
|
||||
if (pad.button != MouseInput::MouseButton::Undefined) {
|
||||
params.Set("engine", "mouse");
|
||||
|
@ -46,7 +46,7 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
|
||||
|
||||
// Don't handle the event if we are configuring
|
||||
if (sdl_state->polling) {
|
||||
sdl_state->event_queue.Push(*event);
|
||||
sdl_state->event_queue.push(*event);
|
||||
} else {
|
||||
sdl_state->HandleGameControllerEvent(*event);
|
||||
}
|
||||
@ -1460,7 +1460,6 @@ public:
|
||||
explicit SDLPoller(SDLState& state_) : state(state_) {}
|
||||
|
||||
void Start([[maybe_unused]] const std::string& device_id) override {
|
||||
state.event_queue.Clear();
|
||||
state.polling = true;
|
||||
}
|
||||
|
||||
@ -1478,7 +1477,7 @@ public:
|
||||
|
||||
Common::ParamPackage GetNextInput() override {
|
||||
SDL_Event event;
|
||||
while (state.event_queue.Pop(event)) {
|
||||
while (state.event_queue.try_pop(event)) {
|
||||
const auto package = FromEvent(event);
|
||||
if (package) {
|
||||
return *package;
|
||||
@ -1550,7 +1549,7 @@ public:
|
||||
|
||||
Common::ParamPackage GetNextInput() override {
|
||||
SDL_Event event;
|
||||
while (state.event_queue.Pop(event)) {
|
||||
while (state.event_queue.try_pop(event)) {
|
||||
const auto package = FromEvent(event);
|
||||
if (package) {
|
||||
return *package;
|
||||
@ -1592,7 +1591,7 @@ public:
|
||||
|
||||
Common::ParamPackage GetNextInput() override {
|
||||
SDL_Event event;
|
||||
while (state.event_queue.Pop(event)) {
|
||||
while (state.event_queue.try_pop(event)) {
|
||||
if (event.type != SDL_JOYAXISMOTION) {
|
||||
// Check for a button press
|
||||
auto button_press = button_poller.FromEvent(event);
|
||||
|
@ -12,8 +12,8 @@
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "common/atomic_threadsafe_queue.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/sdl/sdl.h"
|
||||
|
||||
union SDL_Event;
|
||||
@ -59,7 +59,7 @@ public:
|
||||
|
||||
/// Used by the Pollers during config
|
||||
std::atomic<bool> polling = false;
|
||||
Common::SPSCQueue<SDL_Event> event_queue;
|
||||
Common::MPMCQueue<SDL_Event> event_queue{1024};
|
||||
|
||||
std::vector<Common::ParamPackage> GetInputDevices() override;
|
||||
|
||||
|
@ -338,7 +338,7 @@ void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index,
|
||||
gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2]);
|
||||
}
|
||||
UDPPadStatus pad{
|
||||
.host = clients[client].host,
|
||||
.host = clients[client].host.c_str(),
|
||||
.port = clients[client].port,
|
||||
.pad_index = pad_index,
|
||||
};
|
||||
@ -346,12 +346,12 @@ void Client::UpdateYuzuSettings(std::size_t client, std::size_t pad_index,
|
||||
if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
|
||||
pad.motion = static_cast<PadMotion>(i);
|
||||
pad.motion_value = gyro[i];
|
||||
pad_queue.Push(pad);
|
||||
pad_queue.push(pad);
|
||||
}
|
||||
if (acc[i] > 1.75f || acc[i] < -1.75f) {
|
||||
pad.motion = static_cast<PadMotion>(i + 3);
|
||||
pad.motion_value = acc[i];
|
||||
pad_queue.Push(pad);
|
||||
pad_queue.push(pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,12 +401,10 @@ void Client::UpdateTouchInput(Response::TouchPad& touch_pad, std::size_t client,
|
||||
}
|
||||
|
||||
void Client::BeginConfiguration() {
|
||||
pad_queue.Clear();
|
||||
configuring = true;
|
||||
}
|
||||
|
||||
void Client::EndConfiguration() {
|
||||
pad_queue.Clear();
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
@ -434,11 +432,11 @@ const Input::TouchStatus& Client::GetTouchState() const {
|
||||
return touch_status;
|
||||
}
|
||||
|
||||
Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() {
|
||||
Common::MPMCQueue<UDPPadStatus>& Client::GetPadQueue() {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
const Common::SPSCQueue<UDPPadStatus>& Client::GetPadQueue() const {
|
||||
const Common::MPMCQueue<UDPPadStatus>& Client::GetPadQueue() const {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
@ -471,46 +469,42 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=, this] {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
u16 min_x{UINT16_MAX};
|
||||
u16 min_y{UINT16_MAX};
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
|
||||
Status current_status{Status::Initialized};
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
||||
data.touch[0].y);
|
||||
min_x = std::min(min_x, static_cast<u16>(data.touch[0].x));
|
||||
min_y = std::min(min_y, static_cast<u16>(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
SocketCallback callback{
|
||||
[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
static constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
static constexpr u16 MAX_VALUE = UINT16_MAX;
|
||||
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
const auto& touchpad_0 = data.touch[0];
|
||||
if (touchpad_0.is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
|
||||
const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
|
||||
const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
|
||||
touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes configuration
|
||||
const u16 max_x = touchpad_0.x;
|
||||
const u16 max_y = touchpad_0.y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
|
@ -11,10 +11,10 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include "common/atomic_threadsafe_queue.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/motion_input.h"
|
||||
@ -46,7 +46,7 @@ enum class PadTouch {
|
||||
};
|
||||
|
||||
struct UDPPadStatus {
|
||||
std::string host{"127.0.0.1"};
|
||||
const char* host{"127.0.0.1"};
|
||||
u16 port{26760};
|
||||
std::size_t pad_index{};
|
||||
PadMotion motion{PadMotion::Undefined};
|
||||
@ -85,8 +85,8 @@ public:
|
||||
bool DeviceConnected(std::size_t pad) const;
|
||||
void ReloadSockets();
|
||||
|
||||
Common::SPSCQueue<UDPPadStatus>& GetPadQueue();
|
||||
const Common::SPSCQueue<UDPPadStatus>& GetPadQueue() const;
|
||||
Common::MPMCQueue<UDPPadStatus>& GetPadQueue();
|
||||
const Common::MPMCQueue<UDPPadStatus>& GetPadQueue() const;
|
||||
|
||||
DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad);
|
||||
const DeviceStatus& GetPadState(const std::string& host, u16 port, std::size_t pad) const;
|
||||
@ -146,7 +146,7 @@ private:
|
||||
static constexpr std::size_t MAX_TOUCH_FINGERS = MAX_UDP_CLIENTS * 2;
|
||||
std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{};
|
||||
std::array<ClientConnection, MAX_UDP_CLIENTS> clients{};
|
||||
Common::SPSCQueue<UDPPadStatus> pad_queue{};
|
||||
Common::MPMCQueue<UDPPadStatus> pad_queue{1024};
|
||||
Input::TouchStatus touch_status{};
|
||||
std::array<std::size_t, MAX_TOUCH_FINGERS> finger_id{};
|
||||
};
|
||||
|
@ -59,7 +59,7 @@ Common::ParamPackage UDPMotionFactory::GetNextInput() {
|
||||
Common::ParamPackage params;
|
||||
CemuhookUDP::UDPPadStatus pad;
|
||||
auto& queue = client->GetPadQueue();
|
||||
while (queue.Pop(pad)) {
|
||||
while (queue.try_pop(pad)) {
|
||||
if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
|
||||
continue;
|
||||
}
|
||||
|
@ -32,7 +32,10 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
CommandDataContainer next = state.queue.PopWait(stop_token);
|
||||
CommandDataContainer next;
|
||||
if (!state.queue.try_pop(next)) {
|
||||
continue;
|
||||
}
|
||||
if (stop_token.stop_requested()) {
|
||||
break;
|
||||
}
|
||||
@ -119,7 +122,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
|
||||
|
||||
std::unique_lock lk(state.write_lock);
|
||||
const u64 fence{++state.last_fence};
|
||||
state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
|
||||
state.queue.push(CommandDataContainer(std::move(command_data), fence, block));
|
||||
|
||||
if (block) {
|
||||
state.cv.wait(lk, thread.get_stop_token(), [this, fence] {
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/atomic_threadsafe_queue.h"
|
||||
#include "video_core/framebuffer_config.h"
|
||||
|
||||
namespace Tegra {
|
||||
@ -97,9 +97,9 @@ struct CommandDataContainer {
|
||||
|
||||
/// Struct used to synchronize the GPU thread
|
||||
struct SynchState final {
|
||||
using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
|
||||
using CommandQueue = Common::MPMCQueue<CommandDataContainer>;
|
||||
std::mutex write_lock;
|
||||
CommandQueue queue;
|
||||
CommandQueue queue{100000};
|
||||
u64 last_fence{};
|
||||
std::atomic<u64> signaled_fence{};
|
||||
std::condition_variable_any cv;
|
||||
|
@ -592,8 +592,7 @@ void RasterizerVulkan::EndTransformFeedback() {
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchViewports() &&
|
||||
!state_tracker.ChangedYNegate(regs.screen_y_control.y_negate)) {
|
||||
if (!state_tracker.TouchViewports()) {
|
||||
return;
|
||||
}
|
||||
const std::array viewports{
|
||||
@ -634,10 +633,12 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
regs.zeta.format == Tegra::DepthFormat::D24S8_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::D24C8_UNORM;
|
||||
if (is_d24 && !device.SupportsD24DepthBuffer()) {
|
||||
const double f = static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
|
||||
units = static_cast<float>(static_cast<double>(units) * f);
|
||||
// the base formulas can be obtained from here:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
|
||||
const double rescale_factor =
|
||||
static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
|
||||
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
|
||||
}
|
||||
|
||||
scheduler.Record([constant = units, clamp = regs.polygon_offset_clamp,
|
||||
factor = regs.polygon_offset_factor](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthBias(constant, clamp, factor);
|
||||
|
@ -54,6 +54,7 @@ void SetupDirtyViewports(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
|
||||
FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
|
||||
tables[0][OFF(viewport_transform_enabled)] = Viewports;
|
||||
tables[1][OFF(screen_y_control)] = Viewports;
|
||||
}
|
||||
|
||||
void SetupDirtyScissors(Tables& tables) {
|
||||
|
@ -137,12 +137,6 @@ public:
|
||||
return has_changed;
|
||||
}
|
||||
|
||||
bool ChangedYNegate(u32 new_y_negate) {
|
||||
const bool has_changed = current_y_negate != new_y_negate;
|
||||
current_y_negate = new_y_negate;
|
||||
return has_changed;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
|
||||
|
||||
@ -155,7 +149,6 @@ private:
|
||||
Tegra::Engines::Maxwell3D::DirtyState::Flags& flags;
|
||||
Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
|
||||
Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
|
||||
u32 current_y_negate{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
Loading…
Reference in New Issue
Block a user