diff options
107 files changed, 1079 insertions, 1602 deletions
| diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index 33ff8201f..721179550 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml @@ -8,7 +8,7 @@ steps:    displayName: 'Install vulkan-sdk'  - script: python -m pip install --upgrade pip conan    displayName: 'Install conan' -- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd .. +- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..    displayName: 'Configure CMake'  - task: MSBuild@1    displayName: 'Build' diff --git a/CMakeLists.txt b/CMakeLists.txt index eda555494..5793c5d57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,7 +165,7 @@ macro(yuzu_find_packages)          "lz4               1.8         lz4/1.9.2"          "nlohmann_json     3.8         nlohmann_json/3.8.0"          "ZLIB              1.2         zlib/1.2.11" -        "zstd              1.4         zstd/1.4.5" +        "zstd              1.4         zstd/1.4.8"      )      foreach(PACKAGE ${REQUIRED_LIBS}) diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 4a9a0d07f7376b439430e686721e8176c7b56ce +Subproject 3806284cbefc4115436dcdc687776a45ec31309 diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp index 699fcb84c..3b4144e21 100644 --- a/src/audio_core/algorithm/interpolate.cpp +++ b/src/audio_core/algorithm/interpolate.cpp @@ -218,7 +218,7 @@ void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size          const auto l2 = lut[lut_index + 2];          const auto l3 = lut[lut_index + 3]; -        const auto s0 = static_cast<s32>(input[index]); +        const auto s0 = static_cast<s32>(input[index + 0]);          const auto s1 = static_cast<s32>(input[index + 1]);          const auto s2 = static_cast<s32>(input[index + 2]);          const auto s3 = static_cast<s32>(input[index + 3]); diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 179560cd7..d2ce8c814 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -11,7 +11,6 @@  #include "audio_core/info_updater.h"  #include "audio_core/voice_context.h"  #include "common/logging/log.h" -#include "core/hle/kernel/writable_event.h"  #include "core/memory.h"  #include "core/settings.h" @@ -71,10 +70,9 @@ namespace {  namespace AudioCore {  AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,                               AudioCommon::AudioRendererParameter params, -                             std::shared_ptr<Kernel::WritableEvent> buffer_event_, +                             Stream::ReleaseCallback&& release_callback,                               std::size_t instance_number) -    : worker_params{params}, buffer_event{buffer_event_}, -      memory_pool_info(params.effect_count + params.voice_count * 4), +    : worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4),        voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),        sink_context(params.sink_count), splitter_context(),        voices(params.voice_count), memory{memory_}, @@ -85,10 +83,9 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory                                  params.num_splitter_send_channels);      mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);      audio_out = std::make_unique<AudioCore::AudioOut>(); -    stream = -        audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, -                              fmt::format("AudioRenderer-Instance{}", instance_number), -                              [=]() { buffer_event_->Signal(); }); +    stream = audio_out->OpenStream( +        core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, +        fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));      audio_out->StartStream(stream);      QueueMixedBuffer(0); diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 90f7eafa4..18567f618 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -27,10 +27,6 @@ namespace Core::Timing {  class CoreTiming;  } -namespace Kernel { -class WritableEvent; -} -  namespace Core::Memory {  class Memory;  } @@ -44,8 +40,7 @@ class AudioRenderer {  public:      AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,                    AudioCommon::AudioRendererParameter params, -                  std::shared_ptr<Kernel::WritableEvent> buffer_event_, -                  std::size_t instance_number); +                  Stream::ReleaseCallback&& release_callback, std::size_t instance_number);      ~AudioRenderer();      [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, @@ -61,7 +56,6 @@ private:      BehaviorInfo behavior_info{};      AudioCommon::AudioRendererParameter worker_params; -    std::shared_ptr<Kernel::WritableEvent> buffer_event;      std::vector<ServerMemoryPoolInfo> memory_pool_info;      VoiceContext voice_context;      EffectContext effect_context; diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index eca296589..afe68c9ed 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -130,7 +130,11 @@ bool Stream::ContainsBuffer([[maybe_unused]] Buffer::Tag tag) const {  std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {      std::vector<Buffer::Tag> tags;      for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { -        tags.push_back(released_buffers.front()->GetTag()); +        if (released_buffers.front()) { +            tags.push_back(released_buffers.front()->GetTag()); +        } else { +            ASSERT_MSG(false, "Invalid tag in released_buffers!"); +        }          released_buffers.pop();      }      return tags; @@ -140,7 +144,11 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {      std::vector<Buffer::Tag> tags;      tags.reserve(released_buffers.size());      while (!released_buffers.empty()) { -        tags.push_back(released_buffers.front()->GetTag()); +        if (released_buffers.front()) { +            tags.push_back(released_buffers.front()->GetTag()); +        } else { +            ASSERT_MSG(false, "Invalid tag in released_buffers!"); +        }          released_buffers.pop();      }      return tags; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 943ff996e..2c2bd2ee8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -135,8 +135,6 @@ add_library(common STATIC      math_util.h      memory_detect.cpp      memory_detect.h -    memory_hook.cpp -    memory_hook.h      microprofile.cpp      microprofile.h      microprofileui.h @@ -162,6 +160,8 @@ add_library(common STATIC      thread.cpp      thread.h      thread_queue_list.h +    thread_worker.cpp +    thread_worker.h      threadsafe_queue.h      time_zone.cpp      time_zone.h diff --git a/src/common/memory_hook.cpp b/src/common/memory_hook.cpp deleted file mode 100644 index 3986986d6..000000000 --- a/src/common/memory_hook.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/memory_hook.h" - -namespace Common { - -MemoryHook::~MemoryHook() = default; - -} // namespace Common diff --git a/src/common/memory_hook.h b/src/common/memory_hook.h deleted file mode 100644 index adaa4c2c5..000000000 --- a/src/common/memory_hook.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <memory> -#include <optional> - -#include "common/common_types.h" - -namespace Common { - -/** - * Memory hooks have two purposes: - * 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement - *    texture forwarding and memory breakpoints for debugging. - * 2. To allow for the implementation of MMIO devices. - * - * A hook may be mapped to multiple regions of memory. - * - * If a std::nullopt or false is returned from a function, the read/write request is passed through - * to the underlying memory region. - */ -class MemoryHook { -public: -    virtual ~MemoryHook(); - -    virtual std::optional<bool> IsValidAddress(VAddr addr) = 0; - -    virtual std::optional<u8> Read8(VAddr addr) = 0; -    virtual std::optional<u16> Read16(VAddr addr) = 0; -    virtual std::optional<u32> Read32(VAddr addr) = 0; -    virtual std::optional<u64> Read64(VAddr addr) = 0; - -    virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; - -    virtual bool Write8(VAddr addr, u8 data) = 0; -    virtual bool Write16(VAddr addr, u16 data) = 0; -    virtual bool Write32(VAddr addr, u32 data) = 0; -    virtual bool Write64(VAddr addr, u64 data) = 0; - -    virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0; -}; - -using MemoryHookPointer = std::shared_ptr<MemoryHook>; -} // namespace Common diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index bccea0894..8fd8620fd 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp @@ -10,16 +10,10 @@ PageTable::PageTable() = default;  PageTable::~PageTable() noexcept = default; -void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, -                       bool has_attribute) { -    const std::size_t num_page_table_entries{1ULL -                                             << (address_space_width_in_bits - page_size_in_bits)}; +void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) { +    const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};      pointers.resize(num_page_table_entries);      backing_addr.resize(num_page_table_entries); - -    if (has_attribute) { -        attributes.resize(num_page_table_entries); -    }  }  } // namespace Common diff --git a/src/common/page_table.h b/src/common/page_table.h index 9754fabf9..61c5552e0 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h @@ -4,10 +4,10 @@  #pragma once +#include <atomic>  #include <tuple>  #include "common/common_types.h" -#include "common/memory_hook.h"  #include "common/virtual_buffer.h"  namespace Common { @@ -20,27 +20,6 @@ enum class PageType : u8 {      /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and      /// invalidation      RasterizerCachedMemory, -    /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. -    Special, -    /// Page is allocated for use. -    Allocated, -}; - -struct SpecialRegion { -    enum class Type { -        DebugHook, -        IODevice, -    } type; - -    MemoryHookPointer handler; - -    [[nodiscard]] bool operator<(const SpecialRegion& other) const { -        return std::tie(type, handler) < std::tie(other.type, other.handler); -    } - -    [[nodiscard]] bool operator==(const SpecialRegion& other) const { -        return std::tie(type, handler) == std::tie(other.type, other.handler); -    }  };  /** @@ -48,6 +27,59 @@ struct SpecialRegion {   * mimics the way a real CPU page table works.   */  struct PageTable { +    /// Number of bits reserved for attribute tagging. +    /// This can be at most the guaranteed alignment of the pointers in the page table. +    static constexpr int ATTRIBUTE_BITS = 2; + +    /** +     * Pair of host pointer and page type attribute. +     * This uses the lower bits of a given pointer to store the attribute tag. +     * Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method +     * call. In other words, they are guaranteed to be synchronized at all times. +     */ +    class PageInfo { +    public: +        /// Returns the page pointer +        [[nodiscard]] u8* Pointer() const noexcept { +            return ExtractPointer(raw.load(std::memory_order_relaxed)); +        } + +        /// Returns the page type attribute +        [[nodiscard]] PageType Type() const noexcept { +            return ExtractType(raw.load(std::memory_order_relaxed)); +        } + +        /// Returns the page pointer and attribute pair, extracted from the same atomic read +        [[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept { +            const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed); +            return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)}; +        } + +        /// Returns the raw representation of the page information. +        /// Use ExtractPointer and ExtractType to unpack the value. +        [[nodiscard]] uintptr_t Raw() const noexcept { +            return raw.load(std::memory_order_relaxed); +        } + +        /// Write a page pointer and type pair atomically +        void Store(u8* pointer, PageType type) noexcept { +            raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type)); +        } + +        /// Unpack a pointer from a page info raw representation +        [[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept { +            return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS)); +        } + +        /// Unpack a page type from a page info raw representation +        [[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept { +            return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1)); +        } + +    private: +        std::atomic<uintptr_t> raw; +    }; +      PageTable();      ~PageTable() noexcept; @@ -58,25 +90,21 @@ struct PageTable {      PageTable& operator=(PageTable&&) noexcept = default;      /** -     * Resizes the page table to be able to accomodate enough pages within +     * Resizes the page table to be able to accommodate enough pages within       * a given address space.       *       * @param address_space_width_in_bits The address size width in bits.       * @param page_size_in_bits           The page size in bits. -     * @param has_attribute               Whether or not this page has any backing attributes.       */ -    void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, -                bool has_attribute); +    void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);      /**       * Vector of memory pointers backing each page. An entry can only be non-null if the -     * corresponding entry in the `attributes` vector is of type `Memory`. +     * corresponding attribute element is of type `Memory`.       */ -    VirtualBuffer<u8*> pointers; +    VirtualBuffer<PageInfo> pointers;      VirtualBuffer<u64> backing_addr; - -    VirtualBuffer<PageType> attributes;  };  } // namespace Common diff --git a/src/common/swap.h b/src/common/swap.h index 7665942a2..a80e191dc 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -394,7 +394,7 @@ public:      template <typename S, typename T2, typename F2>      friend S operator%(const S& p, const swapped_t v); -    // Arithmetics + assignements +    // Arithmetics + assignments      template <typename S, typename T2, typename F2>      friend S operator+=(const S& p, const swapped_t v); @@ -451,7 +451,7 @@ S operator%(const S& i, const swap_struct_t<T, F> v) {      return i % v.swap();  } -// Arithmetics + assignements +// Arithmetics + assignments  template <typename S, typename T, typename F>  S& operator+=(S& i, const swap_struct_t<T, F> v) {      i += v.swap(); diff --git a/src/common/thread_worker.cpp b/src/common/thread_worker.cpp new file mode 100644 index 000000000..8f9bf447a --- /dev/null +++ b/src/common/thread_worker.cpp @@ -0,0 +1,58 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/thread.h" +#include "common/thread_worker.h" + +namespace Common { + +ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) { +    for (std::size_t i = 0; i < num_workers; ++i) +        threads.emplace_back([this, thread_name{std::string{name}}] { +            Common::SetCurrentThreadName(thread_name.c_str()); + +            // Wait for first request +            { +                std::unique_lock lock{queue_mutex}; +                condition.wait(lock, [this] { return stop || !requests.empty(); }); +            } + +            while (true) { +                std::function<void()> task; + +                { +                    std::unique_lock lock{queue_mutex}; +                    condition.wait(lock, [this] { return stop || !requests.empty(); }); +                    if (stop || requests.empty()) { +                        return; +                    } +                    task = std::move(requests.front()); +                    requests.pop(); +                } + +                task(); +            } +        }); +} + +ThreadWorker::~ThreadWorker() { +    { +        std::unique_lock lock{queue_mutex}; +        stop = true; +    } +    condition.notify_all(); +    for (std::thread& thread : threads) { +        thread.join(); +    } +} + +void ThreadWorker::QueueWork(std::function<void()>&& work) { +    { +        std::unique_lock lock{queue_mutex}; +        requests.emplace(work); +    } +    condition.notify_one(); +} + +} // namespace Common diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h new file mode 100644 index 000000000..f1859971f --- /dev/null +++ b/src/common/thread_worker.h @@ -0,0 +1,30 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <atomic> +#include <functional> +#include <mutex> +#include <string> +#include <vector> +#include <queue> + +namespace Common { + +class ThreadWorker final { +public: +    explicit ThreadWorker(std::size_t num_workers, const std::string& name); +    ~ThreadWorker(); +    void QueueWork(std::function<void()>&& work); + +private: +    std::vector<std::thread> threads; +    std::queue<std::function<void()>> requests; +    std::mutex queue_mutex; +    std::condition_variable condition; +    std::atomic_bool stop{}; +}; + +} // namespace Common diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h index 91d430036..fb1a6f81f 100644 --- a/src/common/virtual_buffer.h +++ b/src/common/virtual_buffer.h @@ -15,10 +15,12 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;  template <typename T>  class VirtualBuffer final {  public: -    static_assert( -        std::is_trivially_constructible_v<T>, -        "T must be trivially constructible, as non-trivial constructors will not be executed " -        "with the current allocator"); +    // TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible +    // using std::atomic_ref once libc++ has support for it +    // static_assert( +    //     std::is_trivially_constructible_v<T>, +    //     "T must be trivially constructible, as non-trivial constructors will not be executed " +    //     "with the current allocator");      constexpr VirtualBuffer() = default;      explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 59bd3d2a6..01f3e9419 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -202,6 +202,8 @@ add_library(core STATIC      hle/kernel/server_port.h      hle/kernel/server_session.cpp      hle/kernel/server_session.h +    hle/kernel/service_thread.cpp +    hle/kernel/service_thread.h      hle/kernel/session.cpp      hle/kernel/session.h      hle/kernel/shared_memory.cpp @@ -500,7 +502,6 @@ add_library(core STATIC      hle/service/sm/controller.h      hle/service/sm/sm.cpp      hle/service/sm/sm.h -    hle/service/sockets/blocking_worker.h      hle/service/sockets/bsd.cpp      hle/service/sockets/bsd.h      hle/service/sockets/ethc.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index e9c74b1a6..0831dd5d2 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -133,6 +133,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&      config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(          page_table.pointers.data());      config.absolute_offset_page_table = true; +    config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;      config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;      config.only_detect_misalignment_via_page_table_on_page_boundary = true; @@ -180,6 +181,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&          if (Settings::values.cpuopt_unsafe_reduce_fp_error) {              config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;          } +        if (Settings::values.cpuopt_unsafe_inaccurate_nan) { +            config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; +        }      }      return std::make_unique<Dynarmic::A32::Jit>(config); diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 7a4eb88a2..4c5ebca22 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -152,6 +152,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&      // Memory      config.page_table = reinterpret_cast<void**>(page_table.pointers.data());      config.page_table_address_space_bits = address_space_bits; +    config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;      config.silently_mirror_page_table = false;      config.absolute_offset_page_table = true;      config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; @@ -211,6 +212,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&          if (Settings::values.cpuopt_unsafe_reduce_fp_error) {              config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;          } +        if (Settings::values.cpuopt_unsafe_inaccurate_nan) { +            config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; +        }      }      return std::make_shared<Dynarmic::A64::Jit>(config); diff --git a/src/core/core.cpp b/src/core/core.cpp index 0961c0819..1a2002dec 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -159,7 +159,7 @@ struct System::Impl {          device_memory = std::make_unique<Core::DeviceMemory>();          is_multicore = Settings::values.use_multi_core.GetValue(); -        is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); +        is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();          kernel.SetMulticore(is_multicore);          cpu_manager.SetMulticore(is_multicore); @@ -307,7 +307,6 @@ struct System::Impl {          service_manager.reset();          cheat_engine.reset();          telemetry_session.reset(); -        device_memory.reset();          // Close all CPU/threading state          cpu_manager.Shutdown(); diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index e75e80ad0..83decf6cf 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -46,43 +46,6 @@ void SessionRequestHandler::ClientDisconnected(      boost::range::remove_erase(connected_sessions, server_session);  } -std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( -    const std::string& reason, u64 timeout, WakeupCallback&& callback, -    std::shared_ptr<WritableEvent> writable_event) { -    // Put the client thread to sleep until the wait event is signaled or the timeout expires. - -    if (!writable_event) { -        // Create event if not provided -        const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason); -        writable_event = pair.writable; -    } - -    Handle event_handle = InvalidHandle; -    { -        KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout); -        thread->SetHLECallback( -            [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool { -                ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT -                                                ? ThreadWakeupReason::Timeout -                                                : ThreadWakeupReason::Signal; -                callback(thread, context, reason); -                context.WriteToOutgoingCommandBuffer(*thread); -                return true; -            }); -        const auto readable_event{writable_event->GetReadableEvent()}; -        writable_event->Clear(); -        thread->SetHLESyncObject(readable_event.get()); -        thread->SetStatus(ThreadStatus::WaitHLEEvent); -        thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); -        readable_event->AddWaitingThread(thread); -    } -    thread->SetHLETimeEvent(event_handle); - -    is_thread_waiting = true; - -    return writable_event; -} -  HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,                                       std::shared_ptr<ServerSession> server_session,                                       std::shared_ptr<Thread> thread) diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index c31a65476..b112e1ebd 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -129,23 +129,6 @@ public:      using WakeupCallback = std::function<void(          std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>; -    /** -     * Puts the specified guest thread to sleep until the returned event is signaled or until the -     * specified timeout expires. -     * @param reason Reason for pausing the thread, to be used for debugging purposes. -     * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback -     * invoked with a Timeout reason. -     * @param callback Callback to be invoked when the thread is resumed. This callback must write -     * the entire command response once again, regardless of the state of it before this function -     * was called. -     * @param writable_event Event to use to wake up the thread. If unspecified, an event will be -     * created. -     * @returns Event that when signaled will resume the thread and call the callback function. -     */ -    std::shared_ptr<WritableEvent> SleepClientThread( -        const std::string& reason, u64 timeout, WakeupCallback&& callback, -        std::shared_ptr<WritableEvent> writable_event = nullptr); -      /// Populates this context with data from the requesting process/thread.      ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,                                                   u32_le* src_cmdbuf); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 04cae3a43..e8ece8164 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -8,13 +8,14 @@  #include <functional>  #include <memory>  #include <thread> -#include <unordered_map> +#include <unordered_set>  #include <utility>  #include "common/assert.h"  #include "common/logging/log.h"  #include "common/microprofile.h"  #include "common/thread.h" +#include "common/thread_worker.h"  #include "core/arm/arm_interface.h"  #include "core/arm/cpu_interrupt_handler.h"  #include "core/arm/exclusive_monitor.h" @@ -35,6 +36,7 @@  #include "core/hle/kernel/physical_core.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/resource_limit.h" +#include "core/hle/kernel/service_thread.h"  #include "core/hle/kernel/shared_memory.h"  #include "core/hle/kernel/synchronization.h"  #include "core/hle/kernel/thread.h" @@ -60,6 +62,8 @@ struct KernelCore::Impl {          RegisterHostThread();          global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); +        service_thread_manager = +            std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");          InitializePhysicalCores();          InitializeSystemResourceLimit(kernel); @@ -76,6 +80,12 @@ struct KernelCore::Impl {      }      void Shutdown() { +        process_list.clear(); + +        // Ensures all service threads gracefully shutdown +        service_thread_manager.reset(); +        service_threads.clear(); +          next_object_id = 0;          next_kernel_process_id = Process::InitialKIPIDMin;          next_user_process_id = Process::ProcessIDMin; @@ -89,8 +99,6 @@ struct KernelCore::Impl {          cores.clear(); -        process_list.clear(); -          current_process = nullptr;          system_resource_limit = nullptr; @@ -103,10 +111,8 @@ struct KernelCore::Impl {          exclusive_monitor.reset(); -        num_host_threads = 0; -        std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(), -                  std::thread::id{}); -        std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0); +        // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others +        next_host_thread_id = Core::Hardware::NUM_CPU_CORES;      }      void InitializePhysicalCores() { @@ -186,52 +192,46 @@ struct KernelCore::Impl {          }      } +    /// Creates a new host thread ID, should only be called by GetHostThreadId +    u32 AllocateHostThreadId(std::optional<std::size_t> core_id) { +        if (core_id) { +            // The first for slots are reserved for CPU core threads +            ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES); +            return static_cast<u32>(*core_id); +        } else { +            return next_host_thread_id++; +        } +    } + +    /// Gets the host thread ID for the caller, allocating a new one if this is the first time +    u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) { +        const thread_local auto host_thread_id{AllocateHostThreadId(core_id)}; +        return host_thread_id; +    } + +    /// Registers a CPU core thread by allocating a host thread ID for it      void RegisterCoreThread(std::size_t core_id) { -        const std::thread::id this_id = std::this_thread::get_id(); +        ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); +        const auto this_id = GetHostThreadId(core_id);          if (!is_multicore) {              single_core_thread_id = this_id;          } -        const auto end = -            register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads); -        const auto it = std::find(register_host_thread_keys.begin(), end, this_id); -        ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); -        ASSERT(it == end); -        InsertHostThread(static_cast<u32>(core_id));      } +    /// Registers a new host thread by allocating a host thread ID for it      void RegisterHostThread() { -        const std::thread::id this_id = std::this_thread::get_id(); -        const auto end = -            register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads); -        const auto it = std::find(register_host_thread_keys.begin(), end, this_id); -        if (it == end) { -            InsertHostThread(registered_thread_ids++); -        } -    } - -    void InsertHostThread(u32 value) { -        const size_t index = num_host_threads++; -        ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads"); -        register_host_thread_values[index] = value; -        register_host_thread_keys[index] = std::this_thread::get_id(); +        [[maybe_unused]] const auto this_id = GetHostThreadId();      } -    [[nodiscard]] u32 GetCurrentHostThreadID() const { -        const std::thread::id this_id = std::this_thread::get_id(); +    [[nodiscard]] u32 GetCurrentHostThreadID() { +        const auto this_id = GetHostThreadId();          if (!is_multicore && single_core_thread_id == this_id) {              return static_cast<u32>(system.GetCpuManager().CurrentCore());          } -        const auto end = -            register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads); -        const auto it = std::find(register_host_thread_keys.begin(), end, this_id); -        if (it == end) { -            return Core::INVALID_HOST_THREAD_ID; -        } -        return register_host_thread_values[static_cast<size_t>( -            std::distance(register_host_thread_keys.begin(), it))]; +        return this_id;      } -    Core::EmuThreadHandle GetCurrentEmuThreadID() const { +    [[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {          Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();          result.host_handle = GetCurrentHostThreadID();          if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) { @@ -325,15 +325,8 @@ struct KernelCore::Impl {      std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;      std::vector<Kernel::PhysicalCore> cores; -    // 0-3 IDs represent core threads, >3 represent others -    std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; - -    // Number of host threads is a relatively high number to avoid overflowing -    static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64; -    std::atomic<size_t> num_host_threads{0}; -    std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS> -        register_host_thread_keys{}; -    std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{}; +    // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others +    std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};      // Kernel memory management      std::unique_ptr<Memory::MemoryManager> memory_manager; @@ -345,12 +338,19 @@ struct KernelCore::Impl {      std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;      std::shared_ptr<Kernel::SharedMemory> time_shared_mem; +    // Threads used for services +    std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; + +    // Service threads are managed by a worker thread, so that a calling service thread can queue up +    // the release of itself +    std::unique_ptr<Common::ThreadWorker> service_thread_manager; +      std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};      std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};      std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};      bool is_multicore{}; -    std::thread::id single_core_thread_id{}; +    u32 single_core_thread_id{};      std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; @@ -639,4 +639,19 @@ void KernelCore::ExitSVCProfile() {      MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);  } +std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { +    auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name); +    impl->service_thread_manager->QueueWork( +        [this, service_thread] { impl->service_threads.emplace(service_thread); }); +    return service_thread; +} + +void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { +    impl->service_thread_manager->QueueWork([this, service_thread] { +        if (auto strong_ptr = service_thread.lock()) { +            impl->service_threads.erase(strong_ptr); +        } +    }); +} +  } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 5846c3f39..e3169f5a7 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -42,6 +42,7 @@ class Process;  class ResourceLimit;  class KScheduler;  class SharedMemory; +class ServiceThread;  class Synchronization;  class Thread;  class TimeManager; @@ -227,6 +228,22 @@ public:      void ExitSVCProfile(); +    /** +     * Creates an HLE service thread, which are used to execute service routines asynchronously. +     * While these are allocated per ServerSession, these need to be owned and managed outside of +     * ServerSession to avoid a circular dependency. +     * @param name String name for the ServerSession creating this thread, used for debug purposes. +     * @returns The a weak pointer newly created service thread. +     */ +    std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); + +    /** +     * Releases a HLE service thread, instructing KernelCore to free it. This should be called when +     * the ServerSession associated with the thread is destroyed. +     * @param service_thread Service thread to release. +     */ +    void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread); +  private:      friend class Object;      friend class Process; diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h index 37fe19916..83acece1e 100644 --- a/src/core/hle/kernel/memory/memory_block.h +++ b/src/core/hle/kernel/memory/memory_block.h @@ -73,12 +73,12 @@ enum class MemoryState : u32 {      ThreadLocal =          static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, -    Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | -                 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | -                 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, +    Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc | +                  FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | +                  FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, -    SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | -                       FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, +    SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc | +                        FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,      SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |                   FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, @@ -111,8 +111,8 @@ static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);  static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);  static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);  static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); -static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D); -static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E); +static_assert(static_cast<u32>(MemoryState::Transferred) == 0x015C3C0D); +static_assert(static_cast<u32>(MemoryState::SharedTransferred) == 0x005C380E);  static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);  static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);  static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811); diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp index f53a7be82..080886554 100644 --- a/src/core/hle/kernel/memory/page_table.cpp +++ b/src/core/hle/kernel/memory/page_table.cpp @@ -265,7 +265,7 @@ ResultCode PageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_t      physical_memory_usage = 0;      memory_pool = pool; -    page_table_impl.Resize(address_space_width, PageBits, true); +    page_table_impl.Resize(address_space_width, PageBits);      return InitializeMemoryLayout(start, end);  } @@ -1007,8 +1007,8 @@ constexpr VAddr PageTable::GetRegionAddress(MemoryState state) const {      case MemoryState::Shared:      case MemoryState::AliasCode:      case MemoryState::AliasCodeData: -    case MemoryState::Transfered: -    case MemoryState::SharedTransfered: +    case MemoryState::Transferred: +    case MemoryState::SharedTransferred:      case MemoryState::SharedCode:      case MemoryState::GeneratedCode:      case MemoryState::CodeOut: @@ -1042,8 +1042,8 @@ constexpr std::size_t PageTable::GetRegionSize(MemoryState state) const {      case MemoryState::Shared:      case MemoryState::AliasCode:      case MemoryState::AliasCodeData: -    case MemoryState::Transfered: -    case MemoryState::SharedTransfered: +    case MemoryState::Transferred: +    case MemoryState::SharedTransferred:      case MemoryState::SharedCode:      case MemoryState::GeneratedCode:      case MemoryState::CodeOut: @@ -1080,8 +1080,8 @@ constexpr bool PageTable::CanContain(VAddr addr, std::size_t size, MemoryState s      case MemoryState::AliasCodeData:      case MemoryState::Stack:      case MemoryState::ThreadLocal: -    case MemoryState::Transfered: -    case MemoryState::SharedTransfered: +    case MemoryState::Transferred: +    case MemoryState::SharedTransferred:      case MemoryState::SharedCode:      case MemoryState::GeneratedCode:      case MemoryState::CodeOut: diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index a35c8aa4b..b40fe3916 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -25,19 +25,19 @@  namespace Kernel {  ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} -ServerSession::~ServerSession() = default; + +ServerSession::~ServerSession() { +    kernel.ReleaseServiceThread(service_thread); +}  ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,                                                                  std::shared_ptr<Session> parent,                                                                  std::string name) {      std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; -    session->request_event = -        Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) { -            session->CompleteSyncRequest(); -        });      session->name = std::move(name);      session->parent = std::move(parent); +    session->service_thread = kernel.CreateServiceThread(session->name);      return MakeResult(std::move(session));  } @@ -142,16 +142,16 @@ ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,          std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));      context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); -    request_queue.Push(std::move(context)); + +    if (auto strong_ptr = service_thread.lock()) { +        strong_ptr->QueueSyncRequest(*this, std::move(context)); +        return RESULT_SUCCESS; +    }      return RESULT_SUCCESS;  } -ResultCode ServerSession::CompleteSyncRequest() { -    ASSERT(!request_queue.Empty()); - -    auto& context = *request_queue.Front(); - +ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {      ResultCode result = RESULT_SUCCESS;      // If the session has been converted to a domain, handle the domain request      if (IsDomain() && context.HasDomainMessageHeader()) { @@ -177,18 +177,13 @@ ResultCode ServerSession::CompleteSyncRequest() {          }      } -    request_queue.Pop(); -      return result;  }  ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,                                              Core::Memory::Memory& memory,                                              Core::Timing::CoreTiming& core_timing) { -    const ResultCode result = QueueSyncRequest(std::move(thread), memory); -    const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000}; -    core_timing.ScheduleEvent(delay, request_event, {}); -    return result; +    return QueueSyncRequest(std::move(thread), memory);  }  } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index d23e9ec68..e8d1d99ea 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -10,6 +10,7 @@  #include <vector>  #include "common/threadsafe_queue.h" +#include "core/hle/kernel/service_thread.h"  #include "core/hle/kernel/synchronization_object.h"  #include "core/hle/result.h" @@ -43,6 +44,8 @@ class Thread;   * TLS buffer and control is transferred back to it.   */  class ServerSession final : public SynchronizationObject { +    friend class ServiceThread; +  public:      explicit ServerSession(KernelCore& kernel);      ~ServerSession() override; @@ -132,7 +135,7 @@ private:      ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);      /// Completes a sync request from the emulated application. -    ResultCode CompleteSyncRequest(); +    ResultCode CompleteSyncRequest(HLERequestContext& context);      /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an      /// object handle. @@ -163,11 +166,8 @@ private:      /// The name of this session (optional)      std::string name; -    /// Core timing event used to schedule the service request at some point in the future -    std::shared_ptr<Core::Timing::EventType> request_event; - -    /// Queue of scheduled service requests -    Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue; +    /// Thread to dispatch service requests +    std::weak_ptr<ServiceThread> service_thread;  };  } // namespace Kernel diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp new file mode 100644 index 000000000..ee46f3e21 --- /dev/null +++ b/src/core/hle/kernel/service_thread.cpp @@ -0,0 +1,110 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <condition_variable> +#include <functional> +#include <mutex> +#include <thread> +#include <vector> +#include <queue> + +#include "common/assert.h" +#include "common/scope_exit.h" +#include "common/thread.h" +#include "core/core.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/service_thread.h" +#include "core/hle/lock.h" +#include "video_core/renderer_base.h" + +namespace Kernel { + +class ServiceThread::Impl final { +public: +    explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); +    ~Impl(); + +    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context); + +private: +    std::vector<std::thread> threads; +    std::queue<std::function<void()>> requests; +    std::mutex queue_mutex; +    std::condition_variable condition; +    const std::string service_name; +    bool stop{}; +}; + +ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) +    : service_name{name} { +    for (std::size_t i = 0; i < num_threads; ++i) +        threads.emplace_back([this, &kernel] { +            Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); + +            // Wait for first request before trying to acquire a render context +            { +                std::unique_lock lock{queue_mutex}; +                condition.wait(lock, [this] { return stop || !requests.empty(); }); +            } + +            kernel.RegisterHostThread(); + +            while (true) { +                std::function<void()> task; + +                { +                    std::unique_lock lock{queue_mutex}; +                    condition.wait(lock, [this] { return stop || !requests.empty(); }); +                    if (stop || requests.empty()) { +                        return; +                    } +                    task = std::move(requests.front()); +                    requests.pop(); +                } + +                task(); +            } +        }); +} + +void ServiceThread::Impl::QueueSyncRequest(ServerSession& session, +                                           std::shared_ptr<HLERequestContext>&& context) { +    { +        std::unique_lock lock{queue_mutex}; + +        // ServerSession owns the service thread, so we cannot caption a strong pointer here in the +        // event that the ServerSession is terminated. +        std::weak_ptr<ServerSession> weak_ptr{SharedFrom(&session)}; +        requests.emplace([weak_ptr, context{std::move(context)}]() { +            if (auto strong_ptr = weak_ptr.lock()) { +                strong_ptr->CompleteSyncRequest(*context); +            } +        }); +    } +    condition.notify_one(); +} + +ServiceThread::Impl::~Impl() { +    { +        std::unique_lock lock{queue_mutex}; +        stop = true; +    } +    condition.notify_all(); +    for (std::thread& thread : threads) { +        thread.join(); +    } +} + +ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) +    : impl{std::make_unique<Impl>(kernel, num_threads, name)} {} + +ServiceThread::~ServiceThread() = default; + +void ServiceThread::QueueSyncRequest(ServerSession& session, +                                     std::shared_ptr<HLERequestContext>&& context) { +    impl->QueueSyncRequest(session, std::move(context)); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h new file mode 100644 index 000000000..025ab8fb5 --- /dev/null +++ b/src/core/hle/kernel/service_thread.h @@ -0,0 +1,28 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> + +namespace Kernel { + +class HLERequestContext; +class KernelCore; +class ServerSession; + +class ServiceThread final { +public: +    explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); +    ~ServiceThread(); + +    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context); + +private: +    class Impl; +    std::unique_ptr<Impl> impl; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 986724beb..11e1d8e2d 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -23,8 +23,8 @@ enum class MemoryState : u32 {      Ipc = 0x0A,      Stack = 0x0B,      ThreadLocal = 0x0C, -    Transfered = 0x0D, -    SharedTransfered = 0x0E, +    Transferred = 0x0D, +    SharedTransferred = 0x0E,      SharedCode = 0x0F,      Inaccessible = 0x10,      NonSecureIpc = 0x11, diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index cb13210e5..c9808060a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -560,14 +560,14 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest  AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {      on_new_message = -        Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageRecieved"); +        Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageReceived");      on_operation_mode_changed =          Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged");  }  AppletMessageQueue::~AppletMessageQueue() = default; -const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent() const { +const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMessageReceiveEvent() const {      return on_new_message.readable;  } @@ -675,7 +675,7 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {      IPC::ResponseBuilder rb{ctx, 2, 1};      rb.Push(RESULT_SUCCESS); -    rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent()); +    rb.PushCopyObjects(msg_queue->GetMessageReceiveEvent());  }  void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index b1da0d081..f51aca1af 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -55,7 +55,7 @@ public:      explicit AppletMessageQueue(Kernel::KernelCore& kernel);      ~AppletMessageQueue(); -    const std::shared_ptr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; +    const std::shared_ptr<Kernel::ReadableEvent>& GetMessageReceiveEvent() const;      const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const;      void PushMessage(AppletMessage msg);      AppletMessage PopMessage(); diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 145f47ee2..0cd797109 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -70,8 +70,10 @@ public:              Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased");          stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, -                                       audio_params.channel_count, std::move(unique_name), -                                       [this] { buffer_event.writable->Signal(); }); +                                       audio_params.channel_count, std::move(unique_name), [this] { +                                           const auto guard = LockService(); +                                           buffer_event.writable->Signal(); +                                       });      }  private: diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6e7b7316c..c5c22d053 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -49,16 +49,16 @@ public:          system_event =              Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent"); -        renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), system.Memory(), -                                                              audren_params, system_event.writable, -                                                              instance_number); +        renderer = std::make_unique<AudioCore::AudioRenderer>( +            system.CoreTiming(), system.Memory(), audren_params, +            [this]() { +                const auto guard = LockService(); +                system_event.writable->Signal(); +            }, +            instance_number);      }  private: -    void UpdateAudioCallback() { -        system_event.writable->Signal(); -    } -      void GetSampleRate(Kernel::HLERequestContext& ctx) {          LOG_DEBUG(Service_Audio, "called"); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index b3c7234e1..8d95f74e6 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -78,11 +78,13 @@ IAppletResource::IAppletResource(Core::System& system_)      pad_update_event = Core::Timing::CreateEvent(          "HID::UpdatePadCallback",          [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +            const auto guard = LockService();              UpdateControllers(user_data, ns_late);          });      motion_update_event = Core::Timing::CreateEvent(          "HID::MotionPadCallback",          [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +            const auto guard = LockService();              UpdateMotion(user_data, ns_late);          }); diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 44a8bc060..5681599ba 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -31,8 +31,8 @@ public:       * @param output A buffer where the output data will be written to.       * @returns The result code of the ioctl.       */ -    virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                            IoctlCtrl& ctrl) = 0; +    virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, +                            std::vector<u8>& output) = 0;      /**       * Handles an ioctl2 request. @@ -43,8 +43,7 @@ public:       * @returns The result code of the ioctl.       */      virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                            const std::vector<u8>& inline_input, std::vector<u8>& output, -                            IoctlCtrl& ctrl) = 0; +                            const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;      /**       * Handles an ioctl3 request. @@ -55,7 +54,7 @@ public:       * @returns The result code of the ioctl.       */      virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                            std::vector<u8>& inline_output, IoctlCtrl& ctrl) = 0; +                            std::vector<u8>& inline_output) = 0;  protected:      Core::System& system; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 170a7c9a0..ce615c758 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -18,21 +18,20 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de      : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}  nvdisp_disp0 ::~nvdisp_disp0() = default; -NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                              IoctlCtrl& ctrl) { +NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, +                              std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input, -                              const std::vector<u8>& inline_input, std::vector<u8>& output, -                              IoctlCtrl& ctrl) { +                              const std::vector<u8>& inline_input, std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                              std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                              std::vector<u8>& inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index eb7575e40..55a33b7e4 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -20,13 +20,11 @@ public:      explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);      ~nvdisp_disp0() override; -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;      /// Performs a screen flip, drawing the buffer pointed to by the handle.      void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 4e0652c39..6b062e10e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -21,8 +21,8 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_      : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}  nvhost_as_gpu::~nvhost_as_gpu() = default; -NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                               IoctlCtrl& ctrl) { +NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, +                               std::vector<u8>& output) {      switch (command.group) {      case 'A':          switch (command.cmd) { @@ -55,14 +55,13 @@ NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std:  }  NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, -                               const std::vector<u8>& inline_input, std::vector<u8>& output, -                               IoctlCtrl& ctrl) { +                               const std::vector<u8>& inline_input, std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                               std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                               std::vector<u8>& inline_output) {      switch (command.group) {      case 'A':          switch (command.cmd) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 2bd355af9..08035fa0e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -30,13 +30,11 @@ public:      explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);      ~nvhost_as_gpu() override; -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;  private:      class BufferMap final { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 92d31b620..fea3b7b9f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -20,8 +20,7 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,      : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}  nvhost_ctrl::~nvhost_ctrl() = default; -NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                             IoctlCtrl& ctrl) { +NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {      switch (command.group) {      case 0x0:          switch (command.cmd) { @@ -30,9 +29,9 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v          case 0x1c:              return IocCtrlClearEventWait(input, output);          case 0x1d: -            return IocCtrlEventWait(input, output, false, ctrl); +            return IocCtrlEventWait(input, output, false);          case 0x1e: -            return IocCtrlEventWait(input, output, true, ctrl); +            return IocCtrlEventWait(input, output, true);          case 0x1f:              return IocCtrlEventRegister(input, output);          case 0x20: @@ -48,14 +47,13 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v  }  NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input, -                             const std::vector<u8>& inline_input, std::vector<u8>& output, -                             IoctlCtrl& ctrl) { +                             const std::vector<u8>& inline_input, std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                             std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                             std::vector<u8>& inline_outpu) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } @@ -69,7 +67,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector  }  NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, -                                       bool is_async, IoctlCtrl& ctrl) { +                                       bool is_async) {      IocCtrlEventWaitParams params{};      std::memcpy(¶ms, input.data(), sizeof(params));      LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", @@ -141,12 +139,6 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector          params.value |= event_id;          event.event.writable->Clear();          gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); -        if (!is_async && ctrl.fresh_call) { -            ctrl.must_delay = true; -            ctrl.timeout = params.timeout; -            ctrl.event_id = event_id; -            return NvResult::Timeout; -        }          std::memcpy(output.data(), ¶ms, sizeof(params));          return NvResult::Timeout;      } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 107168e21..c5aa1362a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -18,13 +18,11 @@ public:                           SyncpointManager& syncpoint_manager);      ~nvhost_ctrl() override; -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;  private:      struct IocSyncptReadParams { @@ -123,8 +121,7 @@ private:      static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");      NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); -    NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, -                              IoctlCtrl& ctrl); +    NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);      NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);      NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);      NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 647f5907e..0320d3ae2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -16,7 +16,7 @@ nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}  nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;  NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, -                                 std::vector<u8>& output, IoctlCtrl& ctrl) { +                                 std::vector<u8>& output) {      switch (command.group) {      case 'G':          switch (command.cmd) { @@ -48,15 +48,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,  }  NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, -                                 const std::vector<u8>& inline_input, std::vector<u8>& output, -                                 IoctlCtrl& ctrl) { +                                 const std::vector<u8>& inline_input, std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, -                                 std::vector<u8>& output, std::vector<u8>& inline_output, -                                 IoctlCtrl& ctrl) { +                                 std::vector<u8>& output, std::vector<u8>& inline_output) {      switch (command.group) {      case 'G':          switch (command.cmd) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index c2fffe734..137b88238 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -16,13 +16,11 @@ public:      explicit nvhost_ctrl_gpu(Core::System& system);      ~nvhost_ctrl_gpu() override; -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;  private:      struct IoctlGpuCharacteristics { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index b0c2caba5..af8b3d9f1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -23,8 +23,7 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,  nvhost_gpu::~nvhost_gpu() = default; -NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                            IoctlCtrl& ctrl) { +NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {      switch (command.group) {      case 0x0:          switch (command.cmd) { @@ -76,8 +75,7 @@ NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve  };  NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, -                            const std::vector<u8>& inline_input, std::vector<u8>& output, -                            IoctlCtrl& ctrl) { +                            const std::vector<u8>& inline_input, std::vector<u8>& output) {      switch (command.group) {      case 'H':          switch (command.cmd) { @@ -91,7 +89,7 @@ NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,  }  NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                            std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                            std::vector<u8>& inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index aa0048a9d..e0298b4fe 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -26,13 +26,11 @@ public:                          SyncpointManager& syncpoint_manager);      ~nvhost_gpu() override; -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;  private:      enum class CtxObjects : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index b8328c314..d8735491c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -15,8 +15,8 @@ nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_de      : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}  nvhost_nvdec::~nvhost_nvdec() = default; -NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                              IoctlCtrl& ctrl) { +NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, +                              std::vector<u8>& output) {      switch (command.group) {      case 0x0:          switch (command.cmd) { @@ -58,14 +58,13 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::  }  NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input, -                              const std::vector<u8>& inline_input, std::vector<u8>& output, -                              IoctlCtrl& ctrl) { +                              const std::vector<u8>& inline_input, std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                              std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                              std::vector<u8>& inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 884ed6c5b..79b8b6de1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -14,13 +14,11 @@ public:      explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);      ~nvhost_nvdec() override; -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;  };  } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 6f4ab0ab3..2d06955c0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -13,8 +13,8 @@ namespace Service::Nvidia::Devices {  nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}  nvhost_nvjpg::~nvhost_nvjpg() = default; -NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                              IoctlCtrl& ctrl) { +NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, +                              std::vector<u8>& output) {      switch (command.group) {      case 'H':          switch (command.cmd) { @@ -33,14 +33,13 @@ NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::  }  NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input, -                              const std::vector<u8>& inline_input, std::vector<u8>& output, -                              IoctlCtrl& ctrl) { +                              const std::vector<u8>& inline_input, std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                              std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                              std::vector<u8>& inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 6fb99d959..43948d18d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -16,13 +16,11 @@ public:      explicit nvhost_nvjpg(Core::System& system);      ~nvhost_nvjpg() override; -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;  private:      struct IoctlSetNvmapFD { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 55a17f423..805fe86ae 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -15,8 +15,7 @@ nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)  nvhost_vic::~nvhost_vic() = default; -NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                            IoctlCtrl& ctrl) { +NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {      switch (command.group) {      case 0x0:          switch (command.cmd) { @@ -51,14 +50,13 @@ NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve  }  NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input, -                            const std::vector<u8>& inline_input, std::vector<u8>& output, -                            IoctlCtrl& ctrl) { +                            const std::vector<u8>& inline_input, std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                            std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                            std::vector<u8>& inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index 7f4858cd4..b2e11f4d4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -14,12 +14,10 @@ public:      explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);      ~nvhost_vic(); -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;  };  } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 910cfee51..4015a2740 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -19,8 +19,7 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {  nvmap::~nvmap() = default; -NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                       IoctlCtrl& ctrl) { +NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {      switch (command.group) {      case 0x1:          switch (command.cmd) { @@ -49,14 +48,13 @@ NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<  }  NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input, -                       const std::vector<u8>& inline_input, std::vector<u8>& output, -                       IoctlCtrl& ctrl) { +                       const std::vector<u8>& inline_input, std::vector<u8>& output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  }  NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                       std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                       std::vector<u8>& inline_output) {      UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);      return NvResult::NotImplemented;  } diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index c0c2fa5eb..4484bd79f 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -19,13 +19,11 @@ public:      explicit nvmap(Core::System& system);      ~nvmap() override; -    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +    NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;      NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, -                    IoctlCtrl& ctrl) override; +                    const std::vector<u8>& inline_input, std::vector<u8>& output) override;      NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, -                    std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; +                    std::vector<u8>& inline_output) override;      /// Returns the allocated address of an nvmap object given its handle.      VAddr GetObjectAddress(u32 handle) const; diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index d72c531f6..cc23b001c 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -61,32 +61,9 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {      std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));      const auto input_buffer = ctx.ReadBuffer(0); -    IoctlCtrl ctrl{}; - -    const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer, ctrl); -    if (ctrl.must_delay) { -        ctrl.fresh_call = false; -        ctx.SleepClientThread( -            "NVServices::DelayedResponse", ctrl.timeout, -            [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, -                      Kernel::ThreadWakeupReason reason) { -                IoctlCtrl ctrl2{ctrl}; -                std::vector<u8> tmp_output = output_buffer; -                const auto nv_result2 = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output, ctrl2); - -                if (command.is_out != 0) { -                    ctx.WriteBuffer(tmp_output); -                } - -                IPC::ResponseBuilder rb{ctx_, 3}; -                rb.Push(RESULT_SUCCESS); -                rb.PushEnum(nv_result2); -            }, -            nvdrv->GetEventWriteable(ctrl.event_id)); -    } else { -        if (command.is_out != 0) { -            ctx.WriteBuffer(output_buffer); -        } +    const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); +    if (command.is_out != 0) { +        ctx.WriteBuffer(output_buffer);      }      IPC::ResponseBuilder rb{ctx, 3}; @@ -110,36 +87,8 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {      const auto input_inlined_buffer = ctx.ReadBuffer(1);      std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); -    IoctlCtrl ctrl{}; -      const auto nv_result = -        nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer, ctrl); -    if (ctrl.must_delay) { -        ctrl.fresh_call = false; -        ctx.SleepClientThread( -            "NVServices::DelayedResponse", ctrl.timeout, -            [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, -                      Kernel::ThreadWakeupReason reason) { -                IoctlCtrl ctrl2{ctrl}; -                std::vector<u8> tmp_output = output_buffer; -                const auto nv_result2 = nvdrv->Ioctl2(fd, command, input_buffer, -                                                      input_inlined_buffer, tmp_output, ctrl2); - -                if (command.is_out != 0) { -                    ctx.WriteBuffer(tmp_output); -                } - -                IPC::ResponseBuilder rb{ctx_, 3}; -                rb.Push(RESULT_SUCCESS); -                rb.PushEnum(nv_result2); -            }, -            nvdrv->GetEventWriteable(ctrl.event_id)); -    } else { -        if (command.is_out != 0) { -            ctx.WriteBuffer(output_buffer); -        } -    } - +        nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);      if (command.is_out != 0) {          ctx.WriteBuffer(output_buffer);      } @@ -165,36 +114,11 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {      std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));      std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); -    IoctlCtrl ctrl{};      const auto nv_result = -        nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline, ctrl); -    if (ctrl.must_delay) { -        ctrl.fresh_call = false; -        ctx.SleepClientThread( -            "NVServices::DelayedResponse", ctrl.timeout, -            [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, -                      Kernel::ThreadWakeupReason reason) { -                IoctlCtrl ctrl2{ctrl}; -                std::vector<u8> tmp_output = output_buffer; -                std::vector<u8> tmp_output2 = output_buffer; -                const auto nv_result2 = -                    nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output2, ctrl2); - -                if (command.is_out != 0) { -                    ctx.WriteBuffer(tmp_output, 0); -                    ctx.WriteBuffer(tmp_output2, 1); -                } - -                IPC::ResponseBuilder rb{ctx_, 3}; -                rb.Push(RESULT_SUCCESS); -                rb.PushEnum(nv_result2); -            }, -            nvdrv->GetEventWriteable(ctrl.event_id)); -    } else { -        if (command.is_out != 0) { -            ctx.WriteBuffer(output_buffer, 0); -            ctx.WriteBuffer(output_buffer_inline, 1); -        } +        nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline); +    if (command.is_out != 0) { +        ctx.WriteBuffer(output_buffer, 0); +        ctx.WriteBuffer(output_buffer_inline, 1);      }      IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index a3c4ecd85..3294bc0e7 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -97,15 +97,4 @@ union Ioctl {      BitField<31, 1, u32> is_out;  }; -struct IoctlCtrl { -    // First call done to the servioce for services that call itself again after a call. -    bool fresh_call{true}; -    // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep -    bool must_delay{}; -    // Timeout for the delay -    s64 timeout{}; -    // NV Event Id -    s32 event_id{-1}; -}; -  } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 8e0c9f093..e03195afe 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -91,7 +91,7 @@ DeviceFD Module::Open(const std::string& device_name) {  }  NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, -                        std::vector<u8>& output, IoctlCtrl& ctrl) { +                        std::vector<u8>& output) {      if (fd < 0) {          LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);          return NvResult::InvalidState; @@ -104,12 +104,11 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input          return NvResult::NotImplemented;      } -    return itr->second->Ioctl1(command, input, output, ctrl); +    return itr->second->Ioctl1(command, input, output);  }  NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, -                        const std::vector<u8>& inline_input, std::vector<u8>& output, -                        IoctlCtrl& ctrl) { +                        const std::vector<u8>& inline_input, std::vector<u8>& output) {      if (fd < 0) {          LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);          return NvResult::InvalidState; @@ -122,11 +121,11 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input          return NvResult::NotImplemented;      } -    return itr->second->Ioctl2(command, input, inline_input, output, ctrl); +    return itr->second->Ioctl2(command, input, inline_input, output);  }  NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, -                        std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl) { +                        std::vector<u8>& output, std::vector<u8>& inline_output) {      if (fd < 0) {          LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);          return NvResult::InvalidState; @@ -139,7 +138,7 @@ NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input          return NvResult::NotImplemented;      } -    return itr->second->Ioctl3(command, input, output, inline_output, ctrl); +    return itr->second->Ioctl3(command, input, output, inline_output);  }  NvResult Module::Close(DeviceFD fd) { diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 5985d2179..144e657e5 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -119,13 +119,13 @@ public:      /// Sends an ioctl command to the specified file descriptor.      NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, -                    std::vector<u8>& output, IoctlCtrl& ctrl); +                    std::vector<u8>& output);      NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, -                    const std::vector<u8>& inline_input, std::vector<u8>& output, IoctlCtrl& ctrl); +                    const std::vector<u8>& inline_input, std::vector<u8>& output);      NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, -                    std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl); +                    std::vector<u8>& output, std::vector<u8>& inline_output);      /// Closes a device file descriptor and returns operation success.      NvResult Close(DeviceFD fd); diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 377f47e8e..0e6bde9f5 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -25,7 +25,12 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)      ASSERT(slot < buffer_slots);      LOG_WARNING(Service, "Adding graphics buffer {}", slot); -    free_buffers.push_back(slot); +    { +        std::unique_lock lock{queue_mutex}; +        free_buffers.push_back(slot); +    } +    condition.notify_one(); +      buffers[slot] = {          .slot = slot,          .status = Buffer::Status::Free, @@ -41,10 +46,20 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)  std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,                                                                                         u32 height) { +    // Wait for first request before trying to dequeue +    { +        std::unique_lock lock{queue_mutex}; +        condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); +    } -    if (free_buffers.empty()) { +    if (!is_connect) { +        // Buffer was disconnected while the thread was blocked, this is most likely due to +        // emulation being stopped          return std::nullopt;      } + +    std::unique_lock lock{queue_mutex}; +      auto f_itr = free_buffers.begin();      auto slot = buffers.size(); @@ -97,7 +112,11 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult      buffers[slot].multi_fence = multi_fence;      buffers[slot].swap_interval = 0; -    free_buffers.push_back(slot); +    { +        std::unique_lock lock{queue_mutex}; +        free_buffers.push_back(slot); +    } +    condition.notify_one();      buffer_wait_event.writable->Signal();  } @@ -127,15 +146,26 @@ void BufferQueue::ReleaseBuffer(u32 slot) {      ASSERT(buffers[slot].slot == slot);      buffers[slot].status = Buffer::Status::Free; -    free_buffers.push_back(slot); +    { +        std::unique_lock lock{queue_mutex}; +        free_buffers.push_back(slot); +    } +    condition.notify_one();      buffer_wait_event.writable->Signal();  } +void BufferQueue::Connect() { +    queue_sequence.clear(); +    is_connect = true; +} +  void BufferQueue::Disconnect() {      buffers.fill({});      queue_sequence.clear();      buffer_wait_event.writable->Signal(); +    is_connect = false; +    condition.notify_one();  }  u32 BufferQueue::Query(QueryType type) { diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index e610923cb..a2f60d9eb 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -4,7 +4,9 @@  #pragma once +#include <condition_variable>  #include <list> +#include <mutex>  #include <optional>  #include <vector> @@ -99,6 +101,7 @@ public:      void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);      std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();      void ReleaseBuffer(u32 slot); +    void Connect();      void Disconnect();      u32 Query(QueryType type); @@ -106,18 +109,28 @@ public:          return id;      } +    bool IsConnected() const { +        return is_connect; +    } +      std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;      std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const;  private: -    u32 id; -    u64 layer_id; +    BufferQueue(const BufferQueue&) = delete; + +    u32 id{}; +    u64 layer_id{}; +    std::atomic_bool is_connect{};      std::list<u32> free_buffers;      std::array<Buffer, buffer_slots> buffers;      std::list<u32> queue_sequence;      Kernel::EventPair buffer_wait_event; + +    std::mutex queue_mutex; +    std::condition_variable condition;  };  } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 44aa2bdae..4b3581949 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -88,6 +88,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {  }  NVFlinger::~NVFlinger() { +    for (auto& buffer_queue : buffer_queues) { +        buffer_queue->Disconnect(); +    } +      if (system.IsMulticore()) {          is_running = false;          wait_event->Set(); @@ -104,6 +108,8 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {  }  std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { +    const auto guard = Lock(); +      LOG_DEBUG(Service, "Opening \"{}\" display", name);      // TODO(Subv): Currently we only support the Default display. @@ -121,6 +127,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {  }  std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { +    const auto guard = Lock();      auto* const display = FindDisplay(display_id);      if (display == nullptr) { @@ -129,18 +136,22 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {      const u64 layer_id = next_layer_id++;      const u32 buffer_queue_id = next_buffer_queue_id++; -    buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id); -    display->CreateLayer(layer_id, buffer_queues.back()); +    buffer_queues.emplace_back( +        std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id)); +    display->CreateLayer(layer_id, *buffer_queues.back());      return layer_id;  }  void NVFlinger::CloseLayer(u64 layer_id) { +    const auto guard = Lock(); +      for (auto& display : displays) {          display.CloseLayer(layer_id);      }  }  std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const { +    const auto guard = Lock();      const auto* const layer = FindLayer(display_id, layer_id);      if (layer == nullptr) { @@ -151,6 +162,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co  }  std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { +    const auto guard = Lock();      auto* const display = FindDisplay(display_id);      if (display == nullptr) { @@ -160,20 +172,16 @@ std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id)      return display->GetVSyncEvent();  } -BufferQueue& NVFlinger::FindBufferQueue(u32 id) { +BufferQueue* NVFlinger::FindBufferQueue(u32 id) { +    const auto guard = Lock();      const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), -                                  [id](const auto& queue) { return queue.GetId() == id; }); +                                  [id](const auto& queue) { return queue->GetId() == id; }); -    ASSERT(itr != buffer_queues.end()); -    return *itr; -} - -const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const { -    const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), -                                  [id](const auto& queue) { return queue.GetId() == id; }); +    if (itr == buffer_queues.end()) { +        return nullptr; +    } -    ASSERT(itr != buffer_queues.end()); -    return *itr; +    return itr->get();  }  VI::Display* NVFlinger::FindDisplay(u64 display_id) { diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 1ebe949c0..c6765259f 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -75,10 +75,7 @@ public:      [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;      /// Obtains a buffer queue identified by the ID. -    [[nodiscard]] BufferQueue& FindBufferQueue(u32 id); - -    /// Obtains a buffer queue identified by the ID. -    [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const; +    [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);      /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when      /// finished. @@ -86,11 +83,11 @@ public:      [[nodiscard]] s64 GetNextTicks() const; +private:      [[nodiscard]] std::unique_lock<std::mutex> Lock() const {          return std::unique_lock{*guard};      } -private:      /// Finds the display identified by the specified ID.      [[nodiscard]] VI::Display* FindDisplay(u64 display_id); @@ -110,7 +107,7 @@ private:      std::shared_ptr<Nvidia::Module> nvdrv;      std::vector<VI::Display> displays; -    std::vector<BufferQueue> buffer_queues; +    std::vector<std::unique_ptr<BufferQueue>> buffer_queues;      /// Id to use for the next layer that is created, this counter is shared among all displays.      u64 next_layer_id = 1; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index abf3d1ea3..ff2a5b1db 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -95,9 +95,14 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se      : system{system_}, service_name{service_name_}, max_sessions{max_sessions_},        handler_invoker{handler_invoker_} {} -ServiceFrameworkBase::~ServiceFrameworkBase() = default; +ServiceFrameworkBase::~ServiceFrameworkBase() { +    // Wait for other threads to release access before destroying +    const auto guard = LockService(); +}  void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { +    const auto guard = LockService(); +      ASSERT(!port_installed);      auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); @@ -106,6 +111,8 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)  }  void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) { +    const auto guard = LockService(); +      ASSERT(!port_installed);      auto [server_port, client_port] = @@ -115,17 +122,6 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {      port_installed = true;  } -std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) { -    ASSERT(!port_installed); - -    auto [server_port, client_port] = -        Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); -    auto port = MakeResult(std::move(server_port)).Unwrap(); -    port->SetHleHandler(shared_from_this()); -    port_installed = true; -    return client_port; -} -  void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {      handlers.reserve(handlers.size() + n);      for (std::size_t i = 0; i < n; ++i) { @@ -164,6 +160,8 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {  }  ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) { +    const auto guard = LockService(); +      switch (context.GetCommandType()) {      case IPC::CommandType::Close: {          IPC::ResponseBuilder rb{context, 2}; @@ -184,7 +182,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co          UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType());      } -    context.WriteToOutgoingCommandBuffer(context.GetThread()); +    // If emulation was shutdown, we are closing service threads, do not write the response back to +    // memory that may be shutting down as well. +    if (system.IsPoweredOn()) { +        context.WriteToOutgoingCommandBuffer(context.GetThread()); +    }      return RESULT_SUCCESS;  } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 62a182310..916445517 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -5,9 +5,11 @@  #pragma once  #include <cstddef> +#include <mutex>  #include <string>  #include <boost/container/flat_map.hpp>  #include "common/common_types.h" +#include "common/spin_lock.h"  #include "core/hle/kernel/hle_ipc.h"  #include "core/hle/kernel/object.h" @@ -68,11 +70,9 @@ public:      void InstallAsService(SM::ServiceManager& service_manager);      /// Creates a port pair and registers it on the kernel's global port registry.      void InstallAsNamedPort(Kernel::KernelCore& kernel); -    /// Creates and returns an unregistered port for the service. -    std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel); - +    /// Invokes a service request routine.      void InvokeRequest(Kernel::HLERequestContext& ctx); - +    /// Handles a synchronization request for the service.      ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override;  protected: @@ -80,6 +80,11 @@ protected:      template <typename Self>      using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); +    /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. +    [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() { +        return std::scoped_lock{lock_service}; +    } +      /// System context that the service operates under.      Core::System& system; @@ -115,6 +120,9 @@ private:      /// Function used to safely up-cast pointers to the derived class before invoking a handler.      InvokerFn* handler_invoker;      boost::container::flat_map<u32, FunctionInfoBase> handlers; + +    /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. +    Common::SpinLock lock_service;  };  /** diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h deleted file mode 100644 index 2d53e52b6..000000000 --- a/src/core/hle/service/sockets/blocking_worker.h +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <atomic> -#include <memory> -#include <string> -#include <string_view> -#include <thread> -#include <variant> -#include <vector> - -#include <fmt/format.h> - -#include "common/assert.h" -#include "common/microprofile.h" -#include "common/thread.h" -#include "core/core.h" -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/kernel/writable_event.h" - -namespace Service::Sockets { - -/** - * Worker abstraction to execute blocking calls on host without blocking the guest thread - * - * @tparam Service  Service where the work is executed - * @tparam Types Types of work to execute - */ -template <class Service, class... Types> -class BlockingWorker { -    using This = BlockingWorker<Service, Types...>; -    using WorkVariant = std::variant<std::monostate, Types...>; - -public: -    /// Create a new worker -    static std::unique_ptr<This> Create(Core::System& system, Service* service, -                                        std::string_view name) { -        return std::unique_ptr<This>(new This(system, service, name)); -    } - -    ~BlockingWorker() { -        while (!is_available.load(std::memory_order_relaxed)) { -            // Busy wait until work is finished -            std::this_thread::yield(); -        } -        // Monostate means to exit the thread -        work = std::monostate{}; -        work_event.Set(); -        thread.join(); -    } - -    /** -     * Try to capture the worker to send work after a success -     * @returns True when the worker has been successfully captured -     */ -    bool TryCapture() { -        bool expected = true; -        return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed, -                                                  std::memory_order_relaxed); -    } - -    /** -     * Send work to this worker abstraction -     * @see TryCapture must be called before attempting to call this function -     */ -    template <class Work> -    void SendWork(Work new_work) { -        ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured"); -        work = std::move(new_work); -        work_event.Set(); -    } - -    /// Generate a callback for @see SleepClientThread -    template <class Work> -    auto Callback() { -        return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx, -                      Kernel::ThreadWakeupReason reason) { -            ASSERT(reason == Kernel::ThreadWakeupReason::Signal); -            std::get<Work>(work).Response(ctx); -            is_available.store(true); -        }; -    } - -    /// Get kernel event that will be signalled by the worker when the host operation finishes -    std::shared_ptr<Kernel::WritableEvent> KernelEvent() const { -        return kernel_event; -    } - -private: -    explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) { -        auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name)); -        kernel_event = std::move(pair.writable); -        thread = std::thread([this, &system, service, name] { Run(system, service, name); }); -    } - -    void Run(Core::System& system, Service* service, std::string_view name) { -        system.RegisterHostThread(); - -        const std::string thread_name = fmt::format("yuzu:{}", name); -        MicroProfileOnThreadCreate(thread_name.c_str()); -        Common::SetCurrentThreadName(thread_name.c_str()); - -        bool keep_running = true; -        while (keep_running) { -            work_event.Wait(); - -            const auto visit_fn = [service, &keep_running]<typename T>(T&& w) { -                if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) { -                    keep_running = false; -                } else { -                    w.Execute(service); -                } -            }; -            std::visit(visit_fn, work); - -            kernel_event->Signal(); -        } -    } - -    std::thread thread; -    WorkVariant work; -    Common::Event work_event; -    std::shared_ptr<Kernel::WritableEvent> kernel_event; -    std::atomic_bool is_available{true}; -}; - -template <class Service, class... Types> -class BlockingWorkerPool { -    using Worker = BlockingWorker<Service, Types...>; - -public: -    explicit BlockingWorkerPool(Core::System& system_, Service* service_) -        : system{system_}, service{service_} {} - -    /// Returns a captured worker thread, creating new ones if necessary -    Worker* CaptureWorker() { -        for (auto& worker : workers) { -            if (worker->TryCapture()) { -                return worker.get(); -            } -        } -        auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size())); -        [[maybe_unused]] const bool success = new_worker->TryCapture(); -        ASSERT(success); - -        return workers.emplace_back(std::move(new_worker)).get(); -    } - -private: -    Core::System& system; -    Service* const service; - -    std::vector<std::unique_ptr<Worker>> workers; -}; - -} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 67b419503..2b824059d 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -178,13 +178,12 @@ void BSD::Poll(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout); -    ExecuteWork(ctx, "BSD:Poll", timeout != 0, -                PollWork{ -                    .nfds = nfds, -                    .timeout = timeout, -                    .read_buffer = ctx.ReadBuffer(), -                    .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), -                }); +    ExecuteWork(ctx, PollWork{ +                         .nfds = nfds, +                         .timeout = timeout, +                         .read_buffer = ctx.ReadBuffer(), +                         .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), +                     });  }  void BSD::Accept(Kernel::HLERequestContext& ctx) { @@ -193,11 +192,10 @@ void BSD::Accept(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service, "called. fd={}", fd); -    ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd), -                AcceptWork{ -                    .fd = fd, -                    .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), -                }); +    ExecuteWork(ctx, AcceptWork{ +                         .fd = fd, +                         .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), +                     });  }  void BSD::Bind(Kernel::HLERequestContext& ctx) { @@ -215,11 +213,10 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); -    ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd), -                ConnectWork{ -                    .fd = fd, -                    .addr = ctx.ReadBuffer(), -                }); +    ExecuteWork(ctx, ConnectWork{ +                         .fd = fd, +                         .addr = ctx.ReadBuffer(), +                     });  }  void BSD::GetPeerName(Kernel::HLERequestContext& ctx) { @@ -327,12 +324,11 @@ void BSD::Recv(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize()); -    ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd), -                RecvWork{ -                    .fd = fd, -                    .flags = flags, -                    .message = std::vector<u8>(ctx.GetWriteBufferSize()), -                }); +    ExecuteWork(ctx, RecvWork{ +                         .fd = fd, +                         .flags = flags, +                         .message = std::vector<u8>(ctx.GetWriteBufferSize()), +                     });  }  void BSD::RecvFrom(Kernel::HLERequestContext& ctx) { @@ -344,13 +340,12 @@ void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,                ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1)); -    ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd), -                RecvFromWork{ -                    .fd = fd, -                    .flags = flags, -                    .message = std::vector<u8>(ctx.GetWriteBufferSize(0)), -                    .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)), -                }); +    ExecuteWork(ctx, RecvFromWork{ +                         .fd = fd, +                         .flags = flags, +                         .message = std::vector<u8>(ctx.GetWriteBufferSize(0)), +                         .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)), +                     });  }  void BSD::Send(Kernel::HLERequestContext& ctx) { @@ -361,12 +356,11 @@ void BSD::Send(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize()); -    ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd), -                SendWork{ -                    .fd = fd, -                    .flags = flags, -                    .message = ctx.ReadBuffer(), -                }); +    ExecuteWork(ctx, SendWork{ +                         .fd = fd, +                         .flags = flags, +                         .message = ctx.ReadBuffer(), +                     });  }  void BSD::SendTo(Kernel::HLERequestContext& ctx) { @@ -377,13 +371,12 @@ void BSD::SendTo(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,                ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1)); -    ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd), -                SendToWork{ -                    .fd = fd, -                    .flags = flags, -                    .message = ctx.ReadBuffer(0), -                    .addr = ctx.ReadBuffer(1), -                }); +    ExecuteWork(ctx, SendToWork{ +                         .fd = fd, +                         .flags = flags, +                         .message = ctx.ReadBuffer(0), +                         .addr = ctx.ReadBuffer(1), +                     });  }  void BSD::Write(Kernel::HLERequestContext& ctx) { @@ -392,12 +385,11 @@ void BSD::Write(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize()); -    ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd), -                SendWork{ -                    .fd = fd, -                    .flags = 0, -                    .message = ctx.ReadBuffer(), -                }); +    ExecuteWork(ctx, SendWork{ +                         .fd = fd, +                         .flags = 0, +                         .message = ctx.ReadBuffer(), +                     });  }  void BSD::Close(Kernel::HLERequestContext& ctx) { @@ -410,24 +402,9 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {  }  template <typename Work> -void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, -                      bool is_blocking, Work work) { -    if (!is_blocking) { -        work.Execute(this); -        work.Response(ctx); -        return; -    } - -    // Signal a dummy response to make IPC validation happy -    // This will be overwritten by the SleepClientThread callback +void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) { +    work.Execute(this);      work.Response(ctx); - -    auto worker = worker_pool.CaptureWorker(); - -    ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(), -                          worker->Callback<Work>(), worker->KernelEvent()); - -    worker->SendWork(std::move(work));  }  std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) { @@ -807,18 +784,6 @@ bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {      return true;  } -bool BSD::IsBlockingSocket(s32 fd) const noexcept { -    // Inform invalid sockets as non-blocking -    // This way we avoid using a worker thread as it will fail without blocking host -    if (fd > static_cast<s32>(MAX_FD) || fd < 0) { -        return false; -    } -    if (!file_descriptors[fd]) { -        return false; -    } -    return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0; -} -  void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {      IPC::ResponseBuilder rb{ctx, 4}; @@ -827,8 +792,7 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co      rb.PushEnum(bsd_errno);  } -BSD::BSD(Core::System& system_, const char* name) -    : ServiceFramework{system_, name}, worker_pool{system_, this} { +BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {      // clang-format off      static const FunctionInfo functions[] = {          {0, &BSD::RegisterClient, "RegisterClient"}, diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index f14713fc4..6da0bfeb2 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -11,7 +11,6 @@  #include "common/common_types.h"  #include "core/hle/kernel/hle_ipc.h"  #include "core/hle/service/service.h" -#include "core/hle/service/sockets/blocking_worker.h"  #include "core/hle/service/sockets/sockets.h"  namespace Core { @@ -138,8 +137,7 @@ private:      void Close(Kernel::HLERequestContext& ctx);      template <typename Work> -    void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, -                     bool is_blocking, Work work); +    void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);      std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);      std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, @@ -163,15 +161,10 @@ private:      s32 FindFreeFileDescriptorHandle() noexcept;      bool IsFileDescriptorValid(s32 fd) const noexcept; -    bool IsBlockingSocket(s32 fd) const noexcept;      void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;      std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; - -    BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork, -                       SendToWork> -        worker_pool;  };  class BSDCFG final : public ServiceFramework<BSDCFG> { diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 45cfffe06..968cd16b6 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -536,8 +536,7 @@ private:          LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,                    transaction, flags); -        const auto guard = nv_flinger.Lock(); -        auto& buffer_queue = nv_flinger.FindBufferQueue(id); +        auto& buffer_queue = *nv_flinger.FindBufferQueue(id);          switch (transaction) {          case TransactionId::Connect: { @@ -547,6 +546,9 @@ private:                                   Settings::values.resolution_factor.GetValue()),                  static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *                                   Settings::values.resolution_factor.GetValue())}; + +            buffer_queue.Connect(); +              ctx.WriteBuffer(response.Serialize());              break;          } @@ -563,40 +565,25 @@ private:              IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};              const u32 width{request.data.width};              const u32 height{request.data.height}; -            auto result = buffer_queue.DequeueBuffer(width, height); - -            if (result) { -                // Buffer is available -                IGBPDequeueBufferResponseParcel response{result->first, *result->second}; -                ctx.WriteBuffer(response.Serialize()); -            } else { -                // Wait the current thread until a buffer becomes available -                ctx.SleepClientThread( -                    "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, -                    [=, this](std::shared_ptr<Kernel::Thread> thread, -                              Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { -                        // Repeat TransactParcel DequeueBuffer when a buffer is available -                        const auto guard = nv_flinger.Lock(); -                        auto& buffer_queue = nv_flinger.FindBufferQueue(id); -                        auto result = buffer_queue.DequeueBuffer(width, height); -                        ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); - -                        IGBPDequeueBufferResponseParcel response{result->first, *result->second}; -                        ctx.WriteBuffer(response.Serialize()); -                        IPC::ResponseBuilder rb{ctx, 2}; -                        rb.Push(RESULT_SUCCESS); -                    }, -                    buffer_queue.GetWritableBufferWaitEvent()); -            } + +            do { +                if (auto result = buffer_queue.DequeueBuffer(width, height); result) { +                    // Buffer is available +                    IGBPDequeueBufferResponseParcel response{result->first, *result->second}; +                    ctx.WriteBuffer(response.Serialize()); +                    break; +                } +            } while (buffer_queue.IsConnected()); +              break;          }          case TransactionId::RequestBuffer: {              IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};              auto& buffer = buffer_queue.RequestBuffer(request.slot); -              IGBPRequestBufferResponseParcel response{buffer};              ctx.WriteBuffer(response.Serialize()); +              break;          }          case TransactionId::QueueBuffer: { @@ -682,7 +669,7 @@ private:          LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); -        const auto& buffer_queue = nv_flinger.FindBufferQueue(id); +        const auto& buffer_queue = *nv_flinger.FindBufferQueue(id);          // TODO(Subv): Find out what this actually is.          IPC::ResponseBuilder rb{ctx, 2, 1}; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 54a848936..11609682a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -4,7 +4,6 @@  #include <algorithm>  #include <cstring> -#include <mutex>  #include <optional>  #include <utility> @@ -45,44 +44,16 @@ struct Memory::Impl {          MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);      } -    void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, -                     Common::MemoryHookPointer mmio_handler) { -        UNIMPLEMENTED(); -    } -      void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {          ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);          ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);          MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);      } -    void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, -                      Common::MemoryHookPointer hook) { -        UNIMPLEMENTED(); -    } - -    void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, -                         Common::MemoryHookPointer hook) { -        UNIMPLEMENTED(); -    } -      bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {          const auto& page_table = process.PageTable().PageTableImpl(); - -        const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; -        if (page_pointer != nullptr) { -            return true; -        } - -        if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) { -            return true; -        } - -        if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) { -            return false; -        } - -        return false; +        const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType(); +        return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;      }      bool IsValidVirtualAddress(VAddr vaddr) const { @@ -100,17 +71,15 @@ struct Memory::Impl {      }      u8* GetPointer(const VAddr vaddr) const { -        u8* const page_pointer{current_page_table->pointers[vaddr >> PAGE_BITS]}; -        if (page_pointer) { -            return page_pointer + vaddr; +        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); +        if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { +            return pointer + vaddr;          } - -        if (current_page_table->attributes[vaddr >> PAGE_BITS] == -            Common::PageType::RasterizerCachedMemory) { +        const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer); +        if (type == Common::PageType::RasterizerCachedMemory) {              return GetPointerFromRasterizerCachedMemory(vaddr);          } - -        return {}; +        return nullptr;      }      u8 Read8(const VAddr addr) { @@ -222,7 +191,8 @@ struct Memory::Impl {                  std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);              const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); -            switch (page_table.attributes[page_index]) { +            const auto [pointer, type] = page_table.pointers[page_index].PointerType(); +            switch (type) {              case Common::PageType::Unmapped: {                  LOG_ERROR(HW_Memory,                            "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", @@ -231,10 +201,8 @@ struct Memory::Impl {                  break;              }              case Common::PageType::Memory: { -                DEBUG_ASSERT(page_table.pointers[page_index]); - -                const u8* const src_ptr = -                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); +                DEBUG_ASSERT(pointer); +                const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);                  std::memcpy(dest_buffer, src_ptr, copy_amount);                  break;              } @@ -268,7 +236,8 @@ struct Memory::Impl {                  std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);              const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); -            switch (page_table.attributes[page_index]) { +            const auto [pointer, type] = page_table.pointers[page_index].PointerType(); +            switch (type) {              case Common::PageType::Unmapped: {                  LOG_ERROR(HW_Memory,                            "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", @@ -277,10 +246,8 @@ struct Memory::Impl {                  break;              }              case Common::PageType::Memory: { -                DEBUG_ASSERT(page_table.pointers[page_index]); - -                const u8* const src_ptr = -                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); +                DEBUG_ASSERT(pointer); +                const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);                  std::memcpy(dest_buffer, src_ptr, copy_amount);                  break;              } @@ -320,7 +287,8 @@ struct Memory::Impl {                  std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);              const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); -            switch (page_table.attributes[page_index]) { +            const auto [pointer, type] = page_table.pointers[page_index].PointerType(); +            switch (type) {              case Common::PageType::Unmapped: {                  LOG_ERROR(HW_Memory,                            "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", @@ -328,10 +296,8 @@ struct Memory::Impl {                  break;              }              case Common::PageType::Memory: { -                DEBUG_ASSERT(page_table.pointers[page_index]); - -                u8* const dest_ptr = -                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); +                DEBUG_ASSERT(pointer); +                u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);                  std::memcpy(dest_ptr, src_buffer, copy_amount);                  break;              } @@ -364,7 +330,8 @@ struct Memory::Impl {                  std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);              const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); -            switch (page_table.attributes[page_index]) { +            const auto [pointer, type] = page_table.pointers[page_index].PointerType(); +            switch (type) {              case Common::PageType::Unmapped: {                  LOG_ERROR(HW_Memory,                            "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", @@ -372,10 +339,8 @@ struct Memory::Impl {                  break;              }              case Common::PageType::Memory: { -                DEBUG_ASSERT(page_table.pointers[page_index]); - -                u8* const dest_ptr = -                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); +                DEBUG_ASSERT(pointer); +                u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);                  std::memcpy(dest_ptr, src_buffer, copy_amount);                  break;              } @@ -414,7 +379,8 @@ struct Memory::Impl {                  std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);              const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); -            switch (page_table.attributes[page_index]) { +            const auto [pointer, type] = page_table.pointers[page_index].PointerType(); +            switch (type) {              case Common::PageType::Unmapped: {                  LOG_ERROR(HW_Memory,                            "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", @@ -422,10 +388,8 @@ struct Memory::Impl {                  break;              }              case Common::PageType::Memory: { -                DEBUG_ASSERT(page_table.pointers[page_index]); - -                u8* dest_ptr = -                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); +                DEBUG_ASSERT(pointer); +                u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);                  std::memset(dest_ptr, 0, copy_amount);                  break;              } @@ -461,7 +425,8 @@ struct Memory::Impl {                  std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);              const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); -            switch (page_table.attributes[page_index]) { +            const auto [pointer, type] = page_table.pointers[page_index].PointerType(); +            switch (type) {              case Common::PageType::Unmapped: {                  LOG_ERROR(HW_Memory,                            "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", @@ -470,9 +435,8 @@ struct Memory::Impl {                  break;              }              case Common::PageType::Memory: { -                DEBUG_ASSERT(page_table.pointers[page_index]); -                const u8* src_ptr = -                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); +                DEBUG_ASSERT(pointer); +                const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS);                  WriteBlock(process, dest_addr, src_ptr, copy_amount);                  break;              } @@ -498,34 +462,19 @@ struct Memory::Impl {          return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);      } -    struct PageEntry { -        u8* const pointer; -        const Common::PageType attribute; -    }; - -    PageEntry SafePageEntry(std::size_t base) const { -        std::lock_guard lock{rasterizer_cache_guard}; -        return { -            .pointer = current_page_table->pointers[base], -            .attribute = current_page_table->attributes[base], -        }; -    } -      void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { -        std::lock_guard lock{rasterizer_cache_guard};          if (vaddr == 0) {              return;          } -          // Iterate over a contiguous CPU address space, which corresponds to the specified GPU          // address space, marking the region as un/cached. The region is marked un/cached at a          // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size          // is different). This assumes the specified GPU address region is contiguous as well. -        u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; -        for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { -            Common::PageType& page_type{current_page_table->attributes[vaddr >> PAGE_BITS]}; - +        const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; +        for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { +            const Common::PageType page_type{ +                current_page_table->pointers[vaddr >> PAGE_BITS].Type()};              if (cached) {                  // Switch page type to cached if now cached                  switch (page_type) { @@ -534,8 +483,8 @@ struct Memory::Impl {                      // space, for example, a system module need not have a VRAM mapping.                      break;                  case Common::PageType::Memory: -                    page_type = Common::PageType::RasterizerCachedMemory; -                    current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; +                    current_page_table->pointers[vaddr >> PAGE_BITS].Store( +                        nullptr, Common::PageType::RasterizerCachedMemory);                      break;                  case Common::PageType::RasterizerCachedMemory:                      // There can be more than one GPU region mapped per CPU region, so it's common @@ -556,16 +505,16 @@ struct Memory::Impl {                      // that this area is already unmarked as cached.                      break;                  case Common::PageType::RasterizerCachedMemory: { -                    u8* pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; +                    u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};                      if (pointer == nullptr) {                          // It's possible that this function has been called while updating the                          // pagetable after unmapping a VMA. In that case the underlying VMA will no                          // longer exist, and we should just leave the pagetable entry blank. -                        page_type = Common::PageType::Unmapped; +                        current_page_table->pointers[vaddr >> PAGE_BITS].Store( +                            nullptr, Common::PageType::Unmapped);                      } else { -                        current_page_table->pointers[vaddr >> PAGE_BITS] = -                            pointer - (vaddr & ~PAGE_MASK); -                        page_type = Common::PageType::Memory; +                        current_page_table->pointers[vaddr >> PAGE_BITS].Store( +                            pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);                      }                      break;                  } @@ -595,7 +544,7 @@ struct Memory::Impl {              auto& gpu = system.GPU();              for (u64 i = 0; i < size; i++) {                  const auto page = base + i; -                if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) { +                if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {                      gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);                  }              } @@ -610,20 +559,18 @@ struct Memory::Impl {                         "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);              while (base != end) { -                page_table.attributes[base] = type; -                page_table.pointers[base] = nullptr; +                page_table.pointers[base].Store(nullptr, type);                  page_table.backing_addr[base] = 0;                  base += 1;              }          } else {              while (base != end) { -                page_table.pointers[base] = -                    system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS); -                page_table.attributes[base] = type; +                page_table.pointers[base].Store( +                    system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type);                  page_table.backing_addr[base] = target - (base << PAGE_BITS); -                ASSERT_MSG(page_table.pointers[base], +                ASSERT_MSG(page_table.pointers[base].Pointer(),                             "memory mapping base yield a nullptr within the table");                  base += 1; @@ -646,21 +593,13 @@ struct Memory::Impl {      template <typename T>      T Read(const VAddr vaddr) {          // Avoid adding any extra logic to this fast-path block -        if (const u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) { +        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); +        if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {              T value;              std::memcpy(&value, &pointer[vaddr], sizeof(T));              return value;          } - -        // Otherwise, we need to grab the page with a lock, in case it is currently being modified -        const auto entry = SafePageEntry(vaddr >> PAGE_BITS); -        if (entry.pointer) { -            T value; -            std::memcpy(&value, &entry.pointer[vaddr], sizeof(T)); -            return value; -        } - -        switch (entry.attribute) { +        switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {          case Common::PageType::Unmapped:              LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);              return 0; @@ -692,20 +631,12 @@ struct Memory::Impl {      template <typename T>      void Write(const VAddr vaddr, const T data) {          // Avoid adding any extra logic to this fast-path block -        if (u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) { +        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); +        if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {              std::memcpy(&pointer[vaddr], &data, sizeof(T));              return;          } - -        // Otherwise, we need to grab the page with a lock, in case it is currently being modified -        const auto entry = SafePageEntry(vaddr >> PAGE_BITS); -        if (entry.pointer) { -            // Memory was mapped, we are done -            std::memcpy(&entry.pointer[vaddr], &data, sizeof(T)); -            return; -        } - -        switch (entry.attribute) { +        switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {          case Common::PageType::Unmapped:              LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,                        static_cast<u32>(data), vaddr); @@ -726,15 +657,13 @@ struct Memory::Impl {      template <typename T>      bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { -        u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; -        if (page_pointer != nullptr) { +        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); +        if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {              // NOTE: Avoid adding any extra logic to this fast-path block -            auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]); -            return Common::AtomicCompareAndSwap(pointer, data, expected); +            const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]); +            return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);          } - -        const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; -        switch (type) { +        switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {          case Common::PageType::Unmapped:              LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,                        static_cast<u32>(data), vaddr); @@ -755,15 +684,13 @@ struct Memory::Impl {      }      bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { -        u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; -        if (page_pointer != nullptr) { +        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); +        if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {              // NOTE: Avoid adding any extra logic to this fast-path block -            auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]); -            return Common::AtomicCompareAndSwap(pointer, data, expected); +            const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]); +            return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);          } - -        const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; -        switch (type) { +        switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {          case Common::PageType::Unmapped:              LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,                        static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr); @@ -783,7 +710,6 @@ struct Memory::Impl {          return true;      } -    mutable std::mutex rasterizer_cache_guard;      Common::PageTable* current_page_table = nullptr;      Core::System& system;  }; @@ -799,25 +725,10 @@ void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size      impl->MapMemoryRegion(page_table, base, size, target);  } -void Memory::MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, -                         Common::MemoryHookPointer mmio_handler) { -    impl->MapIoRegion(page_table, base, size, std::move(mmio_handler)); -} -  void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {      impl->UnmapRegion(page_table, base, size);  } -void Memory::AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, -                          Common::MemoryHookPointer hook) { -    impl->AddDebugHook(page_table, base, size, std::move(hook)); -} - -void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, -                             Common::MemoryHookPointer hook) { -    impl->RemoveDebugHook(page_table, base, size, std::move(hook)); -} -  bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {      return impl->IsValidVirtualAddress(process, vaddr);  } diff --git a/src/core/memory.h b/src/core/memory.h index 4a1cc63f4..705ebb23d 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -8,7 +8,6 @@  #include <memory>  #include <string>  #include "common/common_types.h" -#include "common/memory_hook.h"  namespace Common {  struct PageTable; @@ -78,17 +77,6 @@ public:      void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target);      /** -     * Maps a region of the emulated process address space as a IO region. -     * -     * @param page_table   The page table of the emulated process. -     * @param base         The address to start mapping at. Must be page-aligned. -     * @param size         The amount of bytes to map. Must be page-aligned. -     * @param mmio_handler The handler that backs the mapping. -     */ -    void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, -                     Common::MemoryHookPointer mmio_handler); - -    /**       * Unmaps a region of the emulated process address space.       *       * @param page_table The page table of the emulated process. @@ -98,28 +86,6 @@ public:      void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);      /** -     * Adds a memory hook to intercept reads and writes to given region of memory. -     * -     * @param page_table The page table of the emulated process -     * @param base       The starting address to apply the hook to. -     * @param size       The size of the memory region to apply the hook to, in bytes. -     * @param hook       The hook to apply to the region of memory. -     */ -    void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, -                      Common::MemoryHookPointer hook); - -    /** -     * Removes a memory hook from a given range of memory. -     * -     * @param page_table The page table of the emulated process. -     * @param base       The starting address to remove the hook from. -     * @param size       The size of the memory region to remove the hook from, in bytes. -     * @param hook       The hook to remove from the specified region of memory. -     */ -    void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, -                         Common::MemoryHookPointer hook); - -    /**       * Checks whether or not the supplied address is a valid virtual       * address for the given process.       * diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 47d9ecf9a..39306509a 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -148,9 +148,4 @@ void RestoreGlobalState(bool is_powered_on) {      values.motion_enabled.SetGlobal(true);  } -void Sanitize() { -    values.use_asynchronous_gpu_emulation.SetValue( -        values.use_asynchronous_gpu_emulation.GetValue() || values.use_multi_core.GetValue()); -} -  } // namespace Settings diff --git a/src/core/settings.h b/src/core/settings.h index d5f8d2b7e..a324530bd 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -131,6 +131,7 @@ struct Values {      bool cpuopt_unsafe_unfuse_fma;      bool cpuopt_unsafe_reduce_fp_error; +    bool cpuopt_unsafe_inaccurate_nan;      // Renderer      Setting<RendererBackend> renderer_backend; @@ -221,7 +222,7 @@ struct Values {      bool disable_macro_jit;      bool extended_logging; -    // Misceallaneous +    // Miscellaneous      std::string log_filter;      bool use_dev_keys; @@ -257,7 +258,4 @@ void LogSettings();  // Restore the global state of all applicable settings in the Values struct  void RestoreGlobalState(bool is_powered_on); -// Fixes settings that are known to cause issues with the emulator -void Sanitize(); -  } // namespace Settings diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index f1256c9da..7a6c545bd 100644 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h @@ -120,17 +120,17 @@ private:      /// For use in initialization, querying devices to find the adapter      void Setup(); -    /// Resets status of all GC controller devices to a disconected state +    /// Resets status of all GC controller devices to a disconnected state      void ResetDevices(); -    /// Resets status of device connected to a disconected state +    /// 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();      /// Captures GC Adapter endpoint address -    /// Returns true if the endpoind was set correctly +    /// Returns true if the endpoint was set correctly      bool GetGCEndpoint(libusb_device* device);      /// For shutting down, clear all data, join all threads, release usb diff --git a/src/input_common/motion_input.cpp b/src/input_common/motion_input.cpp index f77ba535d..6a65f175e 100644 --- a/src/input_common/motion_input.cpp +++ b/src/input_common/motion_input.cpp @@ -129,7 +129,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {              rad_gyro += ki * integral_error;              rad_gyro += kd * derivative_error;          } else { -            // Give more weight to acelerometer values to compensate for the lack of gyro +            // Give more weight to accelerometer values to compensate for the lack of gyro              rad_gyro += 35.0f * kp * real_error;              rad_gyro += 10.0f * ki * integral_error;              rad_gyro += 10.0f * kd * derivative_error; diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h index 65e64bee7..58803c1bf 100644 --- a/src/input_common/mouse/mouse_input.h +++ b/src/input_common/mouse/mouse_input.h @@ -20,7 +20,7 @@ enum class MouseButton {      Left,      Wheel,      Right, -    Foward, +    Forward,      Backward,      Undefined,  }; diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 0b531f698..d32eb732a 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -1030,11 +1030,44 @@ public:          }          return {};      } -    [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const { +    [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) {          switch (event.type) {          case SDL_JOYAXISMOTION: -            if (std::abs(event.jaxis.value / 32767.0) < 0.5) { +            if (!axis_memory.count(event.jaxis.which) || +                !axis_memory[event.jaxis.which].count(event.jaxis.axis)) { +                axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value; +                axis_event_count[event.jaxis.which][event.jaxis.axis] = 1;                  break; +            } else { +                axis_event_count[event.jaxis.which][event.jaxis.axis]++; +                // The joystick and axis exist in our map if we take this branch, so no checks +                // needed +                if (std::abs( +                        (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) / +                        32767.0) < 0.5) { +                    break; +                } else { +                    if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 && +                        IsAxisAtPole(event.jaxis.value) && +                        IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) { +                        // If we have exactly two events and both are near a pole, this is +                        // likely a digital input masquerading as an analog axis; Instead of +                        // trying to look at the direction the axis travelled, assume the first +                        // event was press and the second was release; This should handle most +                        // digital axes while deferring to the direction of travel for analog +                        // axes +                        event.jaxis.value = static_cast<Sint16>( +                            std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis])); +                    } else { +                        // There are more than two events, so this is likely a true analog axis, +                        // check the direction it travelled +                        event.jaxis.value = static_cast<Sint16>(std::copysign( +                            32767, +                            event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis])); +                    } +                    axis_memory.clear(); +                    axis_event_count.clear(); +                }              }              [[fallthrough]];          case SDL_JOYBUTTONUP: @@ -1043,6 +1076,16 @@ public:          }          return std::nullopt;      } + +private: +    // Determine whether an axis value is close to an extreme or center +    // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per +    // axis, which is why the center must be considered a pole +    bool IsAxisAtPole(int16_t value) const { +        return std::abs(value) >= 32767 || std::abs(value) < 327; +    } +    std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory; +    std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count;  };  class SDLMotionPoller final : public SDLPoller { diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index 8686a059c..c5da27a38 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -28,14 +28,14 @@ private:      mutable std::mutex mutex;  }; -/// A motion device factory that creates motion devices from JC Adapter +/// A motion device factory that creates motion devices from a UDP client  UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)      : client(std::move(client_)) {}  /**   * Creates motion device   * @param params contains parameters for creating the device: - *     - "port": the nth jcpad on the adapter + *     - "port": the UDP port number   */  std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {      auto ip = params.Get("ip", "127.0.0.1"); @@ -90,14 +90,14 @@ private:      mutable std::mutex mutex;  }; -/// A motion device factory that creates motion devices from JC Adapter +/// A motion device factory that creates motion devices from a UDP client  UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)      : client(std::move(client_)) {}  /**   * Creates motion device   * @param params contains parameters for creating the device: - *     - "port": the nth jcpad on the adapter + *     - "port": the UDP port number   */  std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {      auto ip = params.Get("ip", "127.0.0.1"); diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index d80b0b688..8a606b448 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -4,8 +4,6 @@ add_executable(tests      common/fibers.cpp      common/param_package.cpp      common/ring_buffer.cpp -    core/arm/arm_test_common.cpp -    core/arm/arm_test_common.h      core/core_timing.cpp      tests.cpp  ) diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index 4757dd2b4..d94492fc6 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -207,7 +207,7 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) {  }  /** This test checks for fiber thread exchange configuration and validates that fibers are - *  that a fiber has been succesfully transfered from one thread to another and that the TLS + *  that a fiber has been successfully transferred from one thread to another and that the TLS   *  region of the thread is kept while changing fibers.   */  TEST_CASE("Fibers::InterExchange", "[common]") { @@ -299,7 +299,7 @@ static void ThreadStart3(u32 id, TestControl3& test_control) {  }  /** This test checks for one two threads racing for starting the same fiber. - *  It checks execution occured in an ordered manner and by no time there were + *  It checks execution occurred in an ordered manner and by no time there were   *  two contexts at the same time.   */  TEST_CASE("Fibers::StartRace", "[common]") { diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp deleted file mode 100644 index e54674d11..000000000 --- a/src/tests/core/arm/arm_test_common.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> - -#include "common/page_table.h" -#include "core/core.h" -#include "core/hle/kernel/memory/page_table.h" -#include "core/hle/kernel/process.h" -#include "core/memory.h" -#include "tests/core/arm/arm_test_common.h" - -namespace ArmTests { - -TestEnvironment::TestEnvironment(bool mutable_memory_) -    : mutable_memory(mutable_memory_), -      test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} { -    auto& system = Core::System::GetInstance(); - -    auto process = Kernel::Process::Create(system, "", Kernel::Process::ProcessType::Userland); -    page_table = &process->PageTable().PageTableImpl(); - -    system.Memory().MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); -    system.Memory().MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); - -    kernel.MakeCurrentProcess(process.get()); -} - -TestEnvironment::~TestEnvironment() { -    auto& system = Core::System::GetInstance(); -    system.Memory().UnmapRegion(*page_table, 0x80000000, 0x80000000); -    system.Memory().UnmapRegion(*page_table, 0x00000000, 0x80000000); -} - -void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { -    SetMemory32(vaddr + 0, static_cast<u32>(value)); -    SetMemory32(vaddr + 4, static_cast<u32>(value >> 32)); -} - -void TestEnvironment::SetMemory32(VAddr vaddr, u32 value) { -    SetMemory16(vaddr + 0, static_cast<u16>(value)); -    SetMemory16(vaddr + 2, static_cast<u16>(value >> 16)); -} - -void TestEnvironment::SetMemory16(VAddr vaddr, u16 value) { -    SetMemory8(vaddr + 0, static_cast<u8>(value)); -    SetMemory8(vaddr + 1, static_cast<u8>(value >> 8)); -} - -void TestEnvironment::SetMemory8(VAddr vaddr, u8 value) { -    test_memory->data[vaddr] = value; -} - -std::vector<WriteRecord> TestEnvironment::GetWriteRecords() const { -    return write_records; -} - -void TestEnvironment::ClearWriteRecords() { -    write_records.clear(); -} - -TestEnvironment::TestMemory::~TestMemory() {} - -std::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) { -    return true; -} - -std::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) { -    const auto iter = data.find(addr); - -    if (iter == data.end()) { -        // Some arbitrary data -        return static_cast<u8>(addr); -    } - -    return iter->second; -} - -std::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) { -    return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8; -} - -std::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) { -    return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16; -} - -std::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) { -    return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; -} - -bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) { -    VAddr addr = src_addr; -    u8* data = static_cast<u8*>(dest_buffer); - -    for (std::size_t i = 0; i < size; i++, addr++, data++) { -        *data = *Read8(addr); -    } - -    return true; -} - -bool TestEnvironment::TestMemory::Write8(VAddr addr, u8 data) { -    env->write_records.emplace_back(8, addr, data); -    if (env->mutable_memory) -        env->SetMemory8(addr, data); -    return true; -} - -bool TestEnvironment::TestMemory::Write16(VAddr addr, u16 data) { -    env->write_records.emplace_back(16, addr, data); -    if (env->mutable_memory) -        env->SetMemory16(addr, data); -    return true; -} - -bool TestEnvironment::TestMemory::Write32(VAddr addr, u32 data) { -    env->write_records.emplace_back(32, addr, data); -    if (env->mutable_memory) -        env->SetMemory32(addr, data); -    return true; -} - -bool TestEnvironment::TestMemory::Write64(VAddr addr, u64 data) { -    env->write_records.emplace_back(64, addr, data); -    if (env->mutable_memory) -        env->SetMemory64(addr, data); -    return true; -} - -bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer, -                                             std::size_t size) { -    VAddr addr = dest_addr; -    const u8* data = static_cast<const u8*>(src_buffer); - -    for (std::size_t i = 0; i < size; i++, addr++, data++) { -        env->write_records.emplace_back(8, addr, *data); -        if (env->mutable_memory) -            env->SetMemory8(addr, *data); -    } - -    return true; -} - -} // namespace ArmTests diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h deleted file mode 100644 index d145dbfcc..000000000 --- a/src/tests/core/arm/arm_test_common.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <tuple> -#include <unordered_map> -#include <vector> - -#include "common/common_types.h" -#include "common/memory_hook.h" -#include "core/hle/kernel/kernel.h" - -namespace Common { -struct PageTable; -} - -namespace ArmTests { - -struct WriteRecord { -    WriteRecord(std::size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {} -    std::size_t size; -    VAddr addr; -    u64 data; -    bool operator==(const WriteRecord& o) const { -        return std::tie(size, addr, data) == std::tie(o.size, o.addr, o.data); -    } -}; - -class TestEnvironment final { -public: -    /* -     * Inititalise test environment -     * @param mutable_memory If false, writes to memory can never be read back. -     *                       (Memory is immutable.) -     */ -    explicit TestEnvironment(bool mutable_memory = false); - -    /// Shutdown test environment -    ~TestEnvironment(); - -    /// Sets value at memory location vaddr. -    void SetMemory8(VAddr vaddr, u8 value); -    void SetMemory16(VAddr vaddr, u16 value); -    void SetMemory32(VAddr vaddr, u32 value); -    void SetMemory64(VAddr vaddr, u64 value); - -    /** -     * Whenever Memory::Write{8,16,32,64} is called within the test environment, -     * a new write-record is made. -     * @returns A vector of write records made since they were last cleared. -     */ -    std::vector<WriteRecord> GetWriteRecords() const; - -    /// Empties the internal write-record store. -    void ClearWriteRecords(); - -private: -    friend struct TestMemory; -    struct TestMemory final : Common::MemoryHook { -        explicit TestMemory(TestEnvironment* env_) : env(env_) {} -        TestEnvironment* env; - -        ~TestMemory() override; - -        std::optional<bool> IsValidAddress(VAddr addr) override; - -        std::optional<u8> Read8(VAddr addr) override; -        std::optional<u16> Read16(VAddr addr) override; -        std::optional<u32> Read32(VAddr addr) override; -        std::optional<u64> Read64(VAddr addr) override; - -        bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override; - -        bool Write8(VAddr addr, u8 data) override; -        bool Write16(VAddr addr, u16 data) override; -        bool Write32(VAddr addr, u32 data) override; -        bool Write64(VAddr addr, u64 data) override; - -        bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) override; - -        std::unordered_map<VAddr, u8> data; -    }; - -    bool mutable_memory; -    std::shared_ptr<TestMemory> test_memory; -    std::vector<WriteRecord> write_records; -    Common::PageTable* page_table = nullptr; -    Kernel::KernelCore kernel; -}; - -} // namespace ArmTests diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index f977cf12b..4bd48f706 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -48,6 +48,7 @@ add_library(video_core STATIC      engines/shader_bytecode.h      engines/shader_header.h      engines/shader_type.h +    framebuffer_config.h      macro/macro.cpp      macro/macro.h      macro/macro_hle.cpp @@ -59,10 +60,6 @@ add_library(video_core STATIC      fence_manager.h      gpu.cpp      gpu.h -    gpu_asynch.cpp -    gpu_asynch.h -    gpu_synch.cpp -    gpu_synch.h      gpu_thread.cpp      gpu_thread.h      guest_driver.cpp diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp index aa8c9f9de..55e632346 100644 --- a/src/video_core/command_classes/vic.cpp +++ b/src/video_core/command_classes/vic.cpp @@ -53,7 +53,7 @@ void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {  void Vic::Execute() {      if (output_surface_luma_address == 0) { -        LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Recieved 0x{:X}", +        LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Received 0x{:X}",                    vic_state.output_surface.luma_offset);          return;      } diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h new file mode 100644 index 000000000..b86c3a757 --- /dev/null +++ b/src/video_core/framebuffer_config.h @@ -0,0 +1,31 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Tegra { + +/** + * Struct describing framebuffer configuration + */ +struct FramebufferConfig { +    enum class PixelFormat : u32 { +        A8B8G8R8_UNORM = 1, +        RGB565_UNORM = 4, +        B8G8R8A8_UNORM = 5, +    }; + +    VAddr address{}; +    u32 offset{}; +    u32 width{}; +    u32 height{}; +    u32 stride{}; +    PixelFormat pixel_format{}; + +    using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags; +    TransformFlags transform_flags{}; +    Common::Rectangle<int> crop_rect; +}; + +} // namespace Tegra diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index e2512a7f2..6ab06775f 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -10,6 +10,7 @@  #include "core/core_timing.h"  #include "core/core_timing_util.h"  #include "core/frontend/emu_window.h" +#include "core/hardware_interrupt_manager.h"  #include "core/memory.h"  #include "core/settings.h"  #include "video_core/engines/fermi_2d.h" @@ -36,7 +37,8 @@ GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_)        kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},        maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)},        kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)}, -      shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {} +      shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_}, +      gpu_thread{system_, is_async_} {}  GPU::~GPU() = default; @@ -198,10 +200,6 @@ void GPU::SyncGuestHost() {      renderer->Rasterizer().SyncGuestHost();  } -void GPU::OnCommandListEnd() { -    renderer->Rasterizer().ReleaseFences(); -} -  enum class GpuSemaphoreOperation {      AcquireEqual = 0x1,      WriteLong = 0x2, @@ -461,4 +459,75 @@ void GPU::ProcessSemaphoreAcquire() {      }  } +void GPU::Start() { +    gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher); +    cpu_context = renderer->GetRenderWindow().CreateSharedContext(); +    cpu_context->MakeCurrent(); +} + +void GPU::ObtainContext() { +    cpu_context->MakeCurrent(); +} + +void GPU::ReleaseContext() { +    cpu_context->DoneCurrent(); +} + +void GPU::PushGPUEntries(Tegra::CommandList&& entries) { +    gpu_thread.SubmitList(std::move(entries)); +} + +void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { +    if (!use_nvdec) { +        return; +    } +    // This condition fires when a video stream ends, clear all intermediary data +    if (entries[0].raw == 0xDEADB33F) { +        cdma_pusher.reset(); +        return; +    } +    if (!cdma_pusher) { +        cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this); +    } + +    // SubmitCommandBuffer would make the nvdec operations async, this is not currently working +    // TODO(ameerj): RE proper async nvdec operation +    // gpu_thread.SubmitCommandBuffer(std::move(entries)); + +    cdma_pusher->Push(std::move(entries)); +    cdma_pusher->DispatchCalls(); +} + +void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { +    gpu_thread.SwapBuffers(framebuffer); +} + +void GPU::FlushRegion(VAddr addr, u64 size) { +    gpu_thread.FlushRegion(addr, size); +} + +void GPU::InvalidateRegion(VAddr addr, u64 size) { +    gpu_thread.InvalidateRegion(addr, size); +} + +void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) { +    gpu_thread.FlushAndInvalidateRegion(addr, size); +} + +void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const { +    auto& interrupt_manager = system.InterruptManager(); +    interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); +} + +void GPU::WaitIdle() const { +    gpu_thread.WaitIdle(); +} + +void GPU::OnCommandListEnd() { +    if (is_async) { +        // This command only applies to asynchronous GPU mode +        gpu_thread.OnCommandListEnd(); +    } +} +  } // namespace Tegra diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 660641d04..d81e38680 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -15,6 +15,8 @@  #include "core/hle/service/nvflinger/buffer_queue.h"  #include "video_core/cdma_pusher.h"  #include "video_core/dma_pusher.h" +#include "video_core/framebuffer_config.h" +#include "video_core/gpu_thread.h"  using CacheAddr = std::uintptr_t;  [[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) { @@ -101,28 +103,6 @@ enum class DepthFormat : u32 {  struct CommandListHeader;  class DebugContext; -/** - * Struct describing framebuffer configuration - */ -struct FramebufferConfig { -    enum class PixelFormat : u32 { -        A8B8G8R8_UNORM = 1, -        RGB565_UNORM = 4, -        B8G8R8A8_UNORM = 5, -    }; - -    VAddr address; -    u32 offset; -    u32 width; -    u32 height; -    u32 stride; -    PixelFormat pixel_format; - -    using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags; -    TransformFlags transform_flags; -    Common::Rectangle<int> crop_rect; -}; -  namespace Engines {  class Fermi2D;  class Maxwell3D; @@ -141,7 +121,7 @@ enum class EngineID {  class MemoryManager; -class GPU { +class GPU final {  public:      struct MethodCall {          u32 method{}; @@ -159,7 +139,7 @@ public:      };      explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_); -    virtual ~GPU(); +    ~GPU();      /// Binds a renderer to the GPU.      void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer); @@ -176,7 +156,7 @@ public:      /// Synchronizes CPU writes with Host GPU memory.      void SyncGuestHost();      /// Signal the ending of command list. -    virtual void OnCommandListEnd(); +    void OnCommandListEnd();      /// Request a host GPU memory flush from the CPU.      [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); @@ -240,7 +220,7 @@ public:      }      // Waits for the GPU to finish working -    virtual void WaitIdle() const = 0; +    void WaitIdle() const;      /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.      void WaitFence(u32 syncpoint_id, u32 value); @@ -330,34 +310,34 @@ public:      /// Performs any additional setup necessary in order to begin GPU emulation.      /// This can be used to launch any necessary threads and register any necessary      /// core timing events. -    virtual void Start() = 0; +    void Start();      /// Obtain the CPU Context -    virtual void ObtainContext() = 0; +    void ObtainContext();      /// Release the CPU Context -    virtual void ReleaseContext() = 0; +    void ReleaseContext();      /// Push GPU command entries to be processed -    virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; +    void PushGPUEntries(Tegra::CommandList&& entries);      /// Push GPU command buffer entries to be processed -    virtual void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) = 0; +    void PushCommandBuffer(Tegra::ChCommandHeaderList& entries);      /// Swap buffers (render frame) -    virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; +    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);      /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory -    virtual void FlushRegion(VAddr addr, u64 size) = 0; +    void FlushRegion(VAddr addr, u64 size);      /// Notify rasterizer that any caches of the specified region should be invalidated -    virtual void InvalidateRegion(VAddr addr, u64 size) = 0; +    void InvalidateRegion(VAddr addr, u64 size);      /// Notify rasterizer that any caches of the specified region should be flushed and invalidated -    virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; +    void FlushAndInvalidateRegion(VAddr addr, u64 size);  protected: -    virtual void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const = 0; +    void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const;  private:      void ProcessBindMethod(const MethodCall& method_call); @@ -427,6 +407,9 @@ private:      std::mutex flush_request_mutex;      const bool is_async; + +    VideoCommon::GPUThread::ThreadManager gpu_thread; +    std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;  };  #define ASSERT_REG_POSITION(field_name, position)                                                  \ diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp deleted file mode 100644 index 6cc091ecd..000000000 --- a/src/video_core/gpu_asynch.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/core.h" -#include "core/hardware_interrupt_manager.h" -#include "video_core/gpu_asynch.h" -#include "video_core/gpu_thread.h" -#include "video_core/renderer_base.h" - -namespace VideoCommon { - -GPUAsynch::GPUAsynch(Core::System& system_, bool use_nvdec_) -    : GPU{system_, true, use_nvdec_}, gpu_thread{system_} {} - -GPUAsynch::~GPUAsynch() = default; - -void GPUAsynch::Start() { -    gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher); -    cpu_context = renderer->GetRenderWindow().CreateSharedContext(); -    cpu_context->MakeCurrent(); -} - -void GPUAsynch::ObtainContext() { -    cpu_context->MakeCurrent(); -} - -void GPUAsynch::ReleaseContext() { -    cpu_context->DoneCurrent(); -} - -void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { -    gpu_thread.SubmitList(std::move(entries)); -} - -void GPUAsynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { -    if (!use_nvdec) { -        return; -    } -    // This condition fires when a video stream ends, clear all intermediary data -    if (entries[0].raw == 0xDEADB33F) { -        cdma_pusher.reset(); -        return; -    } -    if (!cdma_pusher) { -        cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this); -    } - -    // SubmitCommandBuffer would make the nvdec operations async, this is not currently working -    // TODO(ameerj): RE proper async nvdec operation -    // gpu_thread.SubmitCommandBuffer(std::move(entries)); - -    cdma_pusher->Push(std::move(entries)); -    cdma_pusher->DispatchCalls(); -} - -void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { -    gpu_thread.SwapBuffers(framebuffer); -} - -void GPUAsynch::FlushRegion(VAddr addr, u64 size) { -    gpu_thread.FlushRegion(addr, size); -} - -void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) { -    gpu_thread.InvalidateRegion(addr, size); -} - -void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { -    gpu_thread.FlushAndInvalidateRegion(addr, size); -} - -void GPUAsynch::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const { -    auto& interrupt_manager = system.InterruptManager(); -    interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); -} - -void GPUAsynch::WaitIdle() const { -    gpu_thread.WaitIdle(); -} - -void GPUAsynch::OnCommandListEnd() { -    gpu_thread.OnCommandListEnd(); -} - -} // namespace VideoCommon diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h deleted file mode 100644 index a384113f4..000000000 --- a/src/video_core/gpu_asynch.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "video_core/gpu.h" -#include "video_core/gpu_thread.h" - -namespace Core::Frontend { -class GraphicsContext; -} - -namespace VideoCore { -class RendererBase; -} // namespace VideoCore - -namespace VideoCommon { - -/// Implementation of GPU interface that runs the GPU asynchronously -class GPUAsynch final : public Tegra::GPU { -public: -    explicit GPUAsynch(Core::System& system_, bool use_nvdec_); -    ~GPUAsynch() override; - -    void Start() override; -    void ObtainContext() override; -    void ReleaseContext() override; -    void PushGPUEntries(Tegra::CommandList&& entries) override; -    void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override; -    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; -    void FlushRegion(VAddr addr, u64 size) override; -    void InvalidateRegion(VAddr addr, u64 size) override; -    void FlushAndInvalidateRegion(VAddr addr, u64 size) override; -    void WaitIdle() const override; - -    void OnCommandListEnd() override; - -protected: -    void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override; - -private: -    GPUThread::ThreadManager gpu_thread; -    std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; -}; - -} // namespace VideoCommon diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp deleted file mode 100644 index 1e9d4b9b2..000000000 --- a/src/video_core/gpu_synch.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "video_core/gpu_synch.h" -#include "video_core/renderer_base.h" - -namespace VideoCommon { - -GPUSynch::GPUSynch(Core::System& system_, bool use_nvdec_) : GPU{system_, false, use_nvdec_} {} - -GPUSynch::~GPUSynch() = default; - -void GPUSynch::Start() {} - -void GPUSynch::ObtainContext() { -    renderer->Context().MakeCurrent(); -} - -void GPUSynch::ReleaseContext() { -    renderer->Context().DoneCurrent(); -} - -void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { -    dma_pusher->Push(std::move(entries)); -    dma_pusher->DispatchCalls(); -} - -void GPUSynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { -    if (!use_nvdec) { -        return; -    } -    // This condition fires when a video stream ends, clears all intermediary data -    if (entries[0].raw == 0xDEADB33F) { -        cdma_pusher.reset(); -        return; -    } -    if (!cdma_pusher) { -        cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this); -    } -    cdma_pusher->Push(std::move(entries)); -    cdma_pusher->DispatchCalls(); -} - -void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { -    renderer->SwapBuffers(framebuffer); -} - -void GPUSynch::FlushRegion(VAddr addr, u64 size) { -    renderer->Rasterizer().FlushRegion(addr, size); -} - -void GPUSynch::InvalidateRegion(VAddr addr, u64 size) { -    renderer->Rasterizer().InvalidateRegion(addr, size); -} - -void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { -    renderer->Rasterizer().FlushAndInvalidateRegion(addr, size); -} - -} // namespace VideoCommon diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h deleted file mode 100644 index c5904b8db..000000000 --- a/src/video_core/gpu_synch.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "video_core/gpu.h" - -namespace Core::Frontend { -class GraphicsContext; -} - -namespace VideoCore { -class RendererBase; -} // namespace VideoCore - -namespace VideoCommon { - -/// Implementation of GPU interface that runs the GPU synchronously -class GPUSynch final : public Tegra::GPU { -public: -    explicit GPUSynch(Core::System& system_, bool use_nvdec_); -    ~GPUSynch() override; - -    void Start() override; -    void ObtainContext() override; -    void ReleaseContext() override; -    void PushGPUEntries(Tegra::CommandList&& entries) override; -    void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override; -    void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; -    void FlushRegion(VAddr addr, u64 size) override; -    void InvalidateRegion(VAddr addr, u64 size) override; -    void FlushAndInvalidateRegion(VAddr addr, u64 size) override; -    void WaitIdle() const override {} - -protected: -    void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, -                             [[maybe_unused]] u32 value) const override {} -}; - -} // namespace VideoCommon diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index e27218b96..7e490bcc3 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -4,6 +4,7 @@  #include "common/assert.h"  #include "common/microprofile.h" +#include "common/scope_exit.h"  #include "common/thread.h"  #include "core/core.h"  #include "core/frontend/emu_window.h" @@ -21,6 +22,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,                        SynchState& state, Tegra::CDmaPusher& cdma_pusher) {      std::string name = "yuzu:GPU";      MicroProfileOnThreadCreate(name.c_str()); +    SCOPE_EXIT({ MicroProfileOnThreadExit(); }); +      Common::SetCurrentThreadName(name.c_str());      Common::SetCurrentThreadPriority(Common::ThreadPriority::High);      system.RegisterHostThread(); @@ -65,7 +68,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,      }  } -ThreadManager::ThreadManager(Core::System& system_) : system{system_} {} +ThreadManager::ThreadManager(Core::System& system_, bool is_async_) +    : system{system_}, is_async{is_async_} {}  ThreadManager::~ThreadManager() {      if (!thread.joinable()) { @@ -97,19 +101,30 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {  }  void ThreadManager::FlushRegion(VAddr addr, u64 size) { -    if (!Settings::IsGPULevelHigh()) { +    if (!is_async) { +        // Always flush with synchronous GPU mode          PushCommand(FlushRegionCommand(addr, size));          return;      } -    if (!Settings::IsGPULevelExtreme()) { -        return; -    } -    if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) { + +    // Asynchronous GPU mode +    switch (Settings::values.gpu_accuracy.GetValue()) { +    case Settings::GPUAccuracy::Normal: +        PushCommand(FlushRegionCommand(addr, size)); +        break; +    case Settings::GPUAccuracy::High: +        // TODO(bunnei): Is this right? Preserving existing behavior for now +        break; +    case Settings::GPUAccuracy::Extreme: {          auto& gpu = system.GPU();          u64 fence = gpu.RequestFlush(addr, size);          PushCommand(GPUTickCommand());          while (fence > gpu.CurrentFlushRequestFence()) {          } +        break; +    } +    default: +        UNIMPLEMENTED_MSG("Unsupported gpu_accuracy {}", Settings::values.gpu_accuracy.GetValue());      }  } @@ -123,7 +138,8 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {  }  void ThreadManager::WaitIdle() const { -    while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed)) { +    while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed) && +           system.IsPoweredOn()) {      }  } @@ -134,6 +150,12 @@ void ThreadManager::OnCommandListEnd() {  u64 ThreadManager::PushCommand(CommandData&& command_data) {      const u64 fence{++state.last_fence};      state.queue.Push(CommandDataContainer(std::move(command_data), fence)); + +    if (!is_async) { +        // In synchronous GPU mode, block the caller until the command has executed +        WaitIdle(); +    } +      return fence;  } diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index f1c52cd9e..2775629e7 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -10,8 +10,9 @@  #include <optional>  #include <thread>  #include <variant> +  #include "common/threadsafe_queue.h" -#include "video_core/gpu.h" +#include "video_core/framebuffer_config.h"  namespace Tegra {  struct FramebufferConfig; @@ -25,6 +26,10 @@ class GraphicsContext;  class System;  } // namespace Core +namespace VideoCore { +class RendererBase; +} // namespace VideoCore +  namespace VideoCommon::GPUThread {  /// Command to signal to the GPU thread that processing has ended @@ -112,7 +117,7 @@ struct SynchState final {  /// Class used to manage the GPU thread  class ThreadManager final {  public: -    explicit ThreadManager(Core::System& system_); +    explicit ThreadManager(Core::System& system_, bool is_async_);      ~ThreadManager();      /// Creates and starts the GPU thread. @@ -150,6 +155,7 @@ private:      Core::System& system;      std::thread thread;      std::thread::id thread_id; +    const bool is_async;  };  } // namespace VideoCommon::GPUThread diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index fd55ca8a8..9008530d5 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp @@ -484,7 +484,7 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla  }  void VKDevice::ReportLoss() const { -    LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); +    LOG_CRITICAL(Render_Vulkan, "Device loss occurred!");      // Wait for the log to flush and for Nsight Aftermath to dump the results      std::this_thread::sleep_for(std::chrono::seconds{15}); diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 837800bfe..53444e945 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -7,8 +7,6 @@  #include "common/logging/log.h"  #include "core/core.h"  #include "core/settings.h" -#include "video_core/gpu_asynch.h" -#include "video_core/gpu_synch.h"  #include "video_core/renderer_base.h"  #include "video_core/renderer_opengl/renderer_opengl.h"  #include "video_core/renderer_vulkan/renderer_vulkan.h" @@ -39,13 +37,9 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(  namespace VideoCore {  std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { -    std::unique_ptr<Tegra::GPU> gpu;      const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue(); -    if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { -        gpu = std::make_unique<VideoCommon::GPUAsynch>(system, use_nvdec); -    } else { -        gpu = std::make_unique<VideoCommon::GPUSynch>(system, use_nvdec); -    } +    std::unique_ptr<Tegra::GPU> gpu = std::make_unique<Tegra::GPU>( +        system, Settings::values.use_asynchronous_gpu_emulation.GetValue(), use_nvdec);      auto context = emu_window.CreateSharedContext();      const auto scope = context->Acquire(); diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index a15e8ca2a..c680fd2c2 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -535,7 +535,7 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)      // This emulates a delay between disconnecting and reconnecting controllers as some games      // do not respond to a change in controller type if it was instantaneous.      using namespace std::chrono_literals; -    std::this_thread::sleep_for(20ms); +    std::this_thread::sleep_for(60ms);      UpdateController(controller_type, player_index, player_connected);  } diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp index 53a993cf6..8ee03ddb3 100644 --- a/src/yuzu/applets/error.cpp +++ b/src/yuzu/applets/error.cpp @@ -19,7 +19,7 @@ QtErrorDisplay::~QtErrorDisplay() = default;  void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {      callback = std::move(finished);      emit MainWindowDisplayError( -        tr("An error has occured.\nPlease try again or contact the developer of the " +        tr("An error has occurred.\nPlease try again or contact the developer of the "             "software.\n\nError Code: %1-%2 (0x%3)")              .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))              .arg(error.description, 4, 10, QChar::fromLatin1('0')) @@ -32,7 +32,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon      const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());      emit MainWindowDisplayError( -        tr("An error occured on %1 at %2.\nPlease try again or contact the " +        tr("An error occurred on %1 at %2.\nPlease try again or contact the "             "developer of the software.\n\nError Code: %3-%4 (0x%5)")              .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))              .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) @@ -46,7 +46,7 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te                                           std::function<void()> finished) const {      callback = std::move(finished);      emit MainWindowDisplayError( -        tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") +        tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5")              .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))              .arg(error.description, 4, 10, QChar::fromLatin1('0'))              .arg(error.raw, 8, 16, QChar::fromLatin1('0')) diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index 649912557..a470056ef 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp @@ -72,7 +72,7 @@ void CompatDB::Submit() {  void CompatDB::OnTestcaseSubmitted() {      if (!testcase_watcher.result()) {          QMessageBox::critical(this, tr("Communication error"), -                              tr("An error occured while sending the Testcase")); +                              tr("An error occurred while sending the Testcase"));          button(NextButton)->setEnabled(true);          button(NextButton)->setText(tr("Next"));          button(CancelButton)->setVisible(true); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 9fb254986..43cd11ba0 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -764,6 +764,8 @@ void Config::ReadCpuValues() {              ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool();          Settings::values.cpuopt_unsafe_reduce_fp_error =              ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); +        Settings::values.cpuopt_unsafe_inaccurate_nan = +            ReadSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true).toBool();      }      qt_config->endGroup(); @@ -1327,6 +1329,8 @@ void Config::SaveCpuValues() {                       Settings::values.cpuopt_unsafe_unfuse_fma, true);          WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),                       Settings::values.cpuopt_unsafe_reduce_fp_error, true); +        WriteSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), +                     Settings::values.cpuopt_unsafe_inaccurate_nan, true);      }      qt_config->endGroup(); @@ -1589,14 +1593,12 @@ void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool  void Config::Reload() {      ReadValues(); -    Settings::Sanitize();      // To apply default value changes      SaveValues();      Settings::Apply(Core::System::GetInstance());  }  void Config::Save() { -    Settings::Sanitize();      SaveValues();  } diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index 37fcd6adc..d055cbd60 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp @@ -36,6 +36,8 @@ void ConfigureCpu::SetConfiguration() {      ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma);      ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);      ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); +    ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); +    ui->cpuopt_unsafe_inaccurate_nan->setChecked(Settings::values.cpuopt_unsafe_inaccurate_nan);  }  void ConfigureCpu::AccuracyUpdated(int index) { @@ -61,6 +63,7 @@ void ConfigureCpu::ApplyConfiguration() {          static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());      Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked();      Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); +    Settings::values.cpuopt_unsafe_inaccurate_nan = ui->cpuopt_unsafe_inaccurate_nan->isChecked();  }  void ConfigureCpu::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index ebdd2e6e9..bcd0962e9 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui @@ -109,6 +109,18 @@            </property>           </widget>          </item> +        <item> +         <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan"> +          <property name="text"> +           <string>Inaccurate NaN handling</string> +          </property> +          <property name="toolTip"> +           <string> +            <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> +           </string> +          </property> +         </widget> +        </item>         </layout>        </widget>       </item> diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index d9009091b..567a36d9b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -4,6 +4,7 @@  #include <algorithm>  #include <memory> +#include <thread>  #include <QSignalBlocker>  #include <QTimer> @@ -181,8 +182,18 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const {  }  void ConfigureInput::ApplyConfiguration() { -    for (auto controller : player_controllers) { +    for (auto* controller : player_controllers) {          controller->ApplyConfiguration(); +        controller->TryDisconnectSelectedController(); +    } + +    // This emulates a delay between disconnecting and reconnecting controllers as some games +    // do not respond to a change in controller type if it was instantaneous. +    using namespace std::chrono_literals; +    std::this_thread::sleep_for(60ms); + +    for (auto* controller : player_controllers) { +        controller->TryConnectSelectedController();      }      advanced->ApplyConfiguration(); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 3c7500ee3..46ea026e4 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -4,7 +4,6 @@  #include <algorithm>  #include <memory> -#include <thread>  #include <utility>  #include <QGridLayout>  #include <QInputDialog> @@ -576,6 +575,10 @@ void ConfigureInputPlayer::ApplyConfiguration() {      std::transform(motions_param.begin(), motions_param.end(), motions.begin(),                     [](const Common::ParamPackage& param) { return param.Serialize(); }); +} + +void ConfigureInputPlayer::TryConnectSelectedController() { +    auto& player = Settings::values.players.GetValue()[player_index];      const auto controller_type =          GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); @@ -588,15 +591,12 @@ void ConfigureInputPlayer::ApplyConfiguration() {          return;      } -    // Disconnect the controller first. -    UpdateController(controller_type, player_index, false); -      player.controller_type = controller_type;      player.connected = player_connected;      ConfigureVibration::SetVibrationDevices(player_index); -    // Handheld +    // Connect/Disconnect Handheld depending on Player 1's controller configuration.      if (player_index == 0) {          auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];          if (controller_type == Settings::ControllerType::Handheld) { @@ -611,14 +611,26 @@ void ConfigureInputPlayer::ApplyConfiguration() {          return;      } -    // This emulates a delay between disconnecting and reconnecting controllers as some games -    // do not respond to a change in controller type if it was instantaneous. -    using namespace std::chrono_literals; -    std::this_thread::sleep_for(20ms); -      UpdateController(controller_type, player_index, player_connected);  } +void ConfigureInputPlayer::TryDisconnectSelectedController() { +    const auto& player = Settings::values.players.GetValue()[player_index]; + +    const auto controller_type = +        GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); +    const auto player_connected = ui->groupConnectedController->isChecked() && +                                  controller_type != Settings::ControllerType::Handheld; + +    // Do not do anything if the controller configuration has not changed. +    if (player.controller_type == controller_type && player.connected == player_connected) { +        return; +    } + +    // Disconnect the controller first. +    UpdateController(controller_type, player_index, false); +} +  void ConfigureInputPlayer::showEvent(QShowEvent* event) {      if (bottom_row == nullptr) {          return; diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 9c30879a2..c4ae50de7 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -54,6 +54,18 @@ public:      /// Save all button configurations to settings file.      void ApplyConfiguration(); +    /** +     * Attempts to connect the currently selected controller in the HID backend. +     * This function will not do anything if it is not connected in the frontend. +     */ +    void TryConnectSelectedController(); + +    /** +     * Attempts to disconnect the currently selected controller in the HID backend. +     * This function will not do anything if the configuration has not changed. +     */ +    void TryDisconnectSelectedController(); +      /// Set the connection state checkbox (used to sync state).      void ConnectPlayer(bool connected); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 7aa515226..43d64b708 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -142,7 +142,7 @@ constexpr int default_mouse_timeout = 2500;  /**   * "Callouts" are one-time instructional messages shown to the user. In the config settings, there   * is a bitfield "callout_flags" options, used to track if a message has already been shown to the - * user. This is 32-bits - if we have more than 32 callouts, we should retire and recyle old ones. + * user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones.   */  enum class CalloutFlag : uint32_t {      Telemetry = 0x1, @@ -580,9 +580,8 @@ void GMainWindow::InitializeWidgets() {          if (emulation_running) {              return;          } -        const bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() || -                              Settings::values.use_multi_core.GetValue(); -        Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); +        Settings::values.use_asynchronous_gpu_emulation.SetValue( +            !Settings::values.use_asynchronous_gpu_emulation.GetValue());          async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());          Settings::Apply(Core::System::GetInstance());      }); @@ -599,16 +598,13 @@ void GMainWindow::InitializeWidgets() {              return;          }          Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue()); -        const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() || -                              Settings::values.use_multi_core.GetValue(); -        Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); -        async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());          multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());          Settings::Apply(Core::System::GetInstance());      });      multicore_status_button->setText(tr("MULTICORE"));      multicore_status_button->setCheckable(true);      multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); +      statusBar()->insertPermanentWidget(0, multicore_status_button);      statusBar()->insertPermanentWidget(0, async_status_button); @@ -1049,20 +1045,23 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {              break;          default: -            if (static_cast<u32>(result) > -                static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { +            if (result > Core::System::ResultStatus::ErrorLoader) {                  const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);                  const u16 error_id = static_cast<u16>(result) - loader_id;                  const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id);                  LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code); -                QMessageBox::critical( -                    this, -                    tr("Error while loading ROM! ").append(QString::fromStdString(error_code)), -                    QString::fromStdString(fmt::format( -                        "{}<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the " -                        "yuzu quickstart guide</a> to redump your files.<br>You can refer " -                        "to the yuzu wiki</a> or the yuzu Discord</a> for help.", -                        static_cast<Loader::ResultStatus>(error_id)))); + +                const auto title = +                    tr("Error while loading ROM! %1", "%1 signifies a numeric error code.") +                        .arg(QString::fromStdString(error_code)); +                const auto description = +                    tr("%1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the " +                       "yuzu quickstart guide</a> to redump your files.<br>You can refer " +                       "to the yuzu wiki</a> or the yuzu Discord</a> for help.", +                       "%1 signifies a numeric error ID.") +                        .arg(error_id); + +                QMessageBox::critical(this, title, description);              } else {                  QMessageBox::critical(                      this, tr("Error while loading ROM!"), @@ -2533,9 +2532,6 @@ void GMainWindow::UpdateStatusBar() {  void GMainWindow::UpdateStatusButtons() {      dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());      multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); -    Settings::values.use_asynchronous_gpu_emulation.SetValue( -        Settings::values.use_asynchronous_gpu_emulation.GetValue() || -        Settings::values.use_multi_core.GetValue());      async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());      renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==                                         Settings::RendererBackend::Vulkan); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 2497c71ae..39e0d35aa 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -202,7 +202,7 @@ int main(int argc, char** argv) {              const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);              const u16 error_id = static_cast<u16>(load_result) - loader_id;              LOG_CRITICAL(Frontend, -                         "While attempting to load the ROM requested, an error occured. Please " +                         "While attempting to load the ROM requested, an error occurred. Please "                           "refer to the yuzu wiki for more information or the yuzu discord for "                           "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",                           loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 6435ffabb..09cf2ad77 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp @@ -242,7 +242,7 @@ int main(int argc, char** argv) {              const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);              const u16 error_id = static_cast<u16>(load_result) - loader_id;              LOG_CRITICAL(Frontend, -                         "While attempting to load the ROM requested, an error occured. Please " +                         "While attempting to load the ROM requested, an error occurred. Please "                           "refer to the yuzu wiki for more information or the yuzu discord for "                           "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",                           loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); | 
