diff --git a/CMakeLists.txt b/CMakeLists.txt index 123a3082a..eb403205c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/README.md b/README.md index 25e2274a2..fec4d641b 100755 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/common/alignment.h b/src/common/alignment.h index 1b56569d1..8570c7d3c 100755 --- a/src/common/alignment.h +++ b/src/common/alignment.h @@ -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; }; + + template + constexpr bool operator==(const AlignmentAllocator&) const noexcept { + return std::is_same_v && Align == Align2; + } }; } // namespace Common diff --git a/src/common/atomic_threadsafe_queue.h b/src/common/atomic_threadsafe_queue.h new file mode 100755 index 000000000..d2208013b --- /dev/null +++ b/src/common/atomic_threadsafe_queue.h @@ -0,0 +1,240 @@ +/* +Copyright (c) 2020 Erik Rigtorp + +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 +#include +#include // offsetof +#include +#include +#include + +namespace Common { +namespace mpmc { +static constexpr size_t hardwareInterferenceSize = 64; + +template +using AlignedAllocator = std::allocator; + +template +struct Slot { + ~Slot() noexcept { + if (turn & 1) { + destroy(); + } + } + + template + void construct(Args&&... args) noexcept { + static_assert(std::is_nothrow_constructible::value, + "T must be nothrow constructible with Args&&..."); + new (&storage) T(std::forward(args)...); + } + + void destroy() noexcept { + static_assert(std::is_nothrow_destructible::value, "T must be nothrow destructible"); + reinterpret_cast(&storage)->~T(); + } + + T&& move() noexcept { + return reinterpret_cast(storage); + } + + // Align to avoid false sharing between adjacent slots + alignas(hardwareInterferenceSize) std::atomic turn = {0}; + typename std::aligned_storage::type storage; +}; + +template >> +class Queue { +private: + static_assert(std::is_nothrow_copy_assignable::value || + std::is_nothrow_move_assignable::value, + "T must be nothrow copy or move assignable"); + + static_assert(std::is_nothrow_destructible::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(slots_) % alignof(Slot) != 0) { + allocator_.deallocate(slots_, capacity_ + 1); + throw std::bad_alloc(); + } + for (size_t i = 0; i < capacity_; ++i) { + new (&slots_[i]) Slot(); + } + static_assert(alignof(Slot) == hardwareInterferenceSize, + "Slot must be aligned to cache line boundary to prevent false sharing"); + static_assert(sizeof(Slot) % 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(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 + void emplace(Args&&... args) noexcept { + static_assert(std::is_nothrow_constructible::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)...); + slot.turn.store(turn(head) * 2 + 1, std::memory_order_release); + } + + template + bool try_emplace(Args&&... args) noexcept { + static_assert(std::is_nothrow_constructible::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)...); + 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::value, + "T must be nothrow copy constructible"); + emplace(v); + } + + template ::value>::type> + void push(P&& v) noexcept { + emplace(std::forward

(v)); + } + + bool try_push(const T& v) noexcept { + static_assert(std::is_nothrow_copy_constructible::value, + "T must be nothrow copy constructible"); + return try_emplace(v); + } + + template ::value>::type> + bool try_push(P&& v) noexcept { + return try_emplace(std::forward

(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* slots_; + [[no_unique_address]] Allocator allocator_; + + // Align to avoid false sharing between head_ and tail_ + alignas(hardwareInterferenceSize) std::atomic head_; + alignas(hardwareInterferenceSize) std::atomic tail_; +}; +} // namespace mpmc + +template >> +using MPMCQueue = mpmc::Queue; + +} // namespace Common + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 845de724d..e61261f98 100755 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -69,8 +69,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector& input, std::vector relocs(params.relocation_count); std::vector reloc_shifts(params.relocation_count); std::vector syncpt_increments(params.syncpoint_count); - std::vector wait_checks(params.syncpoint_count); - std::vector fences(params.fence_count); + std::vector 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& input, std::vector& input, std::vector& input, std::vector(); - 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(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(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 payload = {rumble_command, p1, p2, p3, p4}; - const int err = - libusb_interrupt_transfer(usb_adapter_handle->get(), output_endpoint, payload.data(), - static_cast(payload.size()), &size, 16); + const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(), + static_cast(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(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 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& Adapter::GetPadQueue() { +Common::MPMCQueue& Adapter::GetPadQueue() { return pad_queue; } -const Common::SPSCQueue& Adapter::GetPadQueue() const { +const Common::MPMCQueue& Adapter::GetPadQueue() const { return pad_queue; } diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index 28dbcbe05..c5a93bbed 100755 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h @@ -3,16 +3,13 @@ // Refer to the license.txt file included. #pragma once - #include #include #include -#include #include #include - +#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 axis_values{}; std::array axis_origin{}; u8 reset_origin_counter{}; @@ -91,8 +85,8 @@ public: void BeginConfiguration(); void EndConfiguration(); - Common::SPSCQueue& GetPadQueue(); - const Common::SPSCQueue& GetPadQueue() const; + Common::MPMCQueue& GetPadQueue(); + const Common::MPMCQueue& 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 usb_adapter_handle; + // Join all threads + void JoinThreads(); + + // Release usb handles + void ClearLibusbHandle(); + + libusb_device_handle* usb_adapter_handle = nullptr; std::array pads; - Common::SPSCQueue pad_queue; + Common::MPMCQueue 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 libusb_ctx; + libusb_context* libusb_ctx; u8 input_endpoint{0}; u8 output_endpoint{0}; diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 1b6ded8d6..b323ff6b0 100755 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -7,7 +7,7 @@ #include #include #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(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(pad.port)); diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp index 3b052ffb2..1c9f497eb 100755 --- a/src/input_common/mouse/mouse_input.cpp +++ b/src/input_common/mouse/mouse_input.cpp @@ -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& Mouse::GetMouseQueue() { +Common::MPMCQueue& Mouse::GetMouseQueue() { return mouse_queue; } -const Common::SPSCQueue& Mouse::GetMouseQueue() const { +const Common::MPMCQueue& Mouse::GetMouseQueue() const { return mouse_queue; } diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h index c8bae99c1..41b47eeb6 100755 --- a/src/input_common/mouse/mouse_input.h +++ b/src/input_common/mouse/mouse_input.h @@ -9,8 +9,8 @@ #include #include +#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& GetMouseQueue(); - [[nodiscard]] const Common::SPSCQueue& GetMouseQueue() const; + [[nodiscard]] Common::MPMCQueue& GetMouseQueue(); + [[nodiscard]] const Common::MPMCQueue& 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 mouse_info; - Common::SPSCQueue mouse_queue; + Common::MPMCQueue mouse_queue{1024}; bool configuring{false}; int mouse_panning_timout{}; }; diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp index 090b26972..f8cf19d11 100755 --- a/src/input_common/mouse/mouse_poller.cpp +++ b/src/input_common/mouse/mouse_poller.cpp @@ -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"); diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index ecb00d428..30556614a 100755 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -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); diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 7a9ad6346..78baa576f 100755 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -12,8 +12,8 @@ #include +#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 polling = false; - Common::SPSCQueue event_queue; + Common::MPMCQueue event_queue{1024}; std::vector GetInputDevices() override; diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 9b0aec797..67d5e6653 100755 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp @@ -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(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(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& Client::GetPadQueue() { +Common::MPMCQueue& Client::GetPadQueue() { return pad_queue; } -const Common::SPSCQueue& Client::GetPadQueue() const { +const Common::MPMCQueue& Client::GetPadQueue() const { return pad_queue; } @@ -471,46 +469,42 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( std::function 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(data.touch[0].x)); - min_y = std::min(min_y, static_cast(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(touchpad_0.x)); + const u16 min_y = std::min(MAX_VALUE, static_cast(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(); diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 380f9bb76..0071da55e 100755 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h @@ -11,10 +11,10 @@ #include #include #include +#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& GetPadQueue(); - const Common::SPSCQueue& GetPadQueue() const; + Common::MPMCQueue& GetPadQueue(); + const Common::MPMCQueue& 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 pads{}; std::array clients{}; - Common::SPSCQueue pad_queue{}; + Common::MPMCQueue pad_queue{1024}; Input::TouchStatus touch_status{}; std::array finger_id{}; }; diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index 9829da6f0..3c3842c48 100755 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -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; } diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 9547f277a..c834e00bb 100755 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -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] { diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 00984188e..af29563a8 100755 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -11,7 +11,7 @@ #include #include -#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; + using CommandQueue = Common::MPMCQueue; std::mutex write_lock; - CommandQueue queue; + CommandQueue queue{100000}; u64 last_fence{}; std::atomic signaled_fence{}; std::condition_variable_any cv; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b6496e8f5..30b47a7a0 100755 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -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(1ULL << (32 - 24)) / (static_cast(0x1.ep+127)); - units = static_cast(static_cast(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(1ULL << (32 - 24)) / (static_cast(0x1.ep+127)); + units = static_cast(static_cast(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); diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index e3b7dd61c..c00913f55 100755 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -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) { diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 801d05afa..2f2d6b31f 100755 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -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(~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