early-access version 2153

main
pineappleEA 2021-10-24 03:54:44 +02:00
parent fe662edf4a
commit 238cebb24e
22 changed files with 462 additions and 246 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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(), &params, 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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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};

View File

@ -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));

View File

@ -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;
}

View File

@ -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{};
};

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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{};
};

View File

@ -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;
}

View File

@ -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] {

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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