diff options
Diffstat (limited to 'src')
104 files changed, 1077 insertions, 1600 deletions
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 | |||
| 218 | const auto l2 = lut[lut_index + 2]; | 218 | const auto l2 = lut[lut_index + 2]; |
| 219 | const auto l3 = lut[lut_index + 3]; | 219 | const auto l3 = lut[lut_index + 3]; |
| 220 | 220 | ||
| 221 | const auto s0 = static_cast<s32>(input[index]); | 221 | const auto s0 = static_cast<s32>(input[index + 0]); |
| 222 | const auto s1 = static_cast<s32>(input[index + 1]); | 222 | const auto s1 = static_cast<s32>(input[index + 1]); |
| 223 | const auto s2 = static_cast<s32>(input[index + 2]); | 223 | const auto s2 = static_cast<s32>(input[index + 2]); |
| 224 | const auto s3 = static_cast<s32>(input[index + 3]); | 224 | 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 @@ | |||
| 11 | #include "audio_core/info_updater.h" | 11 | #include "audio_core/info_updater.h" |
| 12 | #include "audio_core/voice_context.h" | 12 | #include "audio_core/voice_context.h" |
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "core/hle/kernel/writable_event.h" | ||
| 15 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 16 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 17 | 16 | ||
| @@ -71,10 +70,9 @@ namespace { | |||
| 71 | namespace AudioCore { | 70 | namespace AudioCore { |
| 72 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, | 71 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, |
| 73 | AudioCommon::AudioRendererParameter params, | 72 | AudioCommon::AudioRendererParameter params, |
| 74 | std::shared_ptr<Kernel::WritableEvent> buffer_event_, | 73 | Stream::ReleaseCallback&& release_callback, |
| 75 | std::size_t instance_number) | 74 | std::size_t instance_number) |
| 76 | : worker_params{params}, buffer_event{buffer_event_}, | 75 | : worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4), |
| 77 | memory_pool_info(params.effect_count + params.voice_count * 4), | ||
| 78 | voice_context(params.voice_count), effect_context(params.effect_count), mix_context(), | 76 | voice_context(params.voice_count), effect_context(params.effect_count), mix_context(), |
| 79 | sink_context(params.sink_count), splitter_context(), | 77 | sink_context(params.sink_count), splitter_context(), |
| 80 | voices(params.voice_count), memory{memory_}, | 78 | voices(params.voice_count), memory{memory_}, |
| @@ -85,10 +83,9 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory | |||
| 85 | params.num_splitter_send_channels); | 83 | params.num_splitter_send_channels); |
| 86 | mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count); | 84 | mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count); |
| 87 | audio_out = std::make_unique<AudioCore::AudioOut>(); | 85 | audio_out = std::make_unique<AudioCore::AudioOut>(); |
| 88 | stream = | 86 | stream = audio_out->OpenStream( |
| 89 | audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, | 87 | core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, |
| 90 | fmt::format("AudioRenderer-Instance{}", instance_number), | 88 | fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); |
| 91 | [=]() { buffer_event_->Signal(); }); | ||
| 92 | audio_out->StartStream(stream); | 89 | audio_out->StartStream(stream); |
| 93 | 90 | ||
| 94 | QueueMixedBuffer(0); | 91 | 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 { | |||
| 27 | class CoreTiming; | 27 | class CoreTiming; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | namespace Kernel { | ||
| 31 | class WritableEvent; | ||
| 32 | } | ||
| 33 | |||
| 34 | namespace Core::Memory { | 30 | namespace Core::Memory { |
| 35 | class Memory; | 31 | class Memory; |
| 36 | } | 32 | } |
| @@ -44,8 +40,7 @@ class AudioRenderer { | |||
| 44 | public: | 40 | public: |
| 45 | AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, | 41 | AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, |
| 46 | AudioCommon::AudioRendererParameter params, | 42 | AudioCommon::AudioRendererParameter params, |
| 47 | std::shared_ptr<Kernel::WritableEvent> buffer_event_, | 43 | Stream::ReleaseCallback&& release_callback, std::size_t instance_number); |
| 48 | std::size_t instance_number); | ||
| 49 | ~AudioRenderer(); | 44 | ~AudioRenderer(); |
| 50 | 45 | ||
| 51 | [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, | 46 | [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, |
| @@ -61,7 +56,6 @@ private: | |||
| 61 | BehaviorInfo behavior_info{}; | 56 | BehaviorInfo behavior_info{}; |
| 62 | 57 | ||
| 63 | AudioCommon::AudioRendererParameter worker_params; | 58 | AudioCommon::AudioRendererParameter worker_params; |
| 64 | std::shared_ptr<Kernel::WritableEvent> buffer_event; | ||
| 65 | std::vector<ServerMemoryPoolInfo> memory_pool_info; | 59 | std::vector<ServerMemoryPoolInfo> memory_pool_info; |
| 66 | VoiceContext voice_context; | 60 | VoiceContext voice_context; |
| 67 | EffectContext effect_context; | 61 | 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 { | |||
| 130 | std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) { | 130 | std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) { |
| 131 | std::vector<Buffer::Tag> tags; | 131 | std::vector<Buffer::Tag> tags; |
| 132 | for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { | 132 | for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { |
| 133 | tags.push_back(released_buffers.front()->GetTag()); | 133 | if (released_buffers.front()) { |
| 134 | tags.push_back(released_buffers.front()->GetTag()); | ||
| 135 | } else { | ||
| 136 | ASSERT_MSG(false, "Invalid tag in released_buffers!"); | ||
| 137 | } | ||
| 134 | released_buffers.pop(); | 138 | released_buffers.pop(); |
| 135 | } | 139 | } |
| 136 | return tags; | 140 | return tags; |
| @@ -140,7 +144,11 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() { | |||
| 140 | std::vector<Buffer::Tag> tags; | 144 | std::vector<Buffer::Tag> tags; |
| 141 | tags.reserve(released_buffers.size()); | 145 | tags.reserve(released_buffers.size()); |
| 142 | while (!released_buffers.empty()) { | 146 | while (!released_buffers.empty()) { |
| 143 | tags.push_back(released_buffers.front()->GetTag()); | 147 | if (released_buffers.front()) { |
| 148 | tags.push_back(released_buffers.front()->GetTag()); | ||
| 149 | } else { | ||
| 150 | ASSERT_MSG(false, "Invalid tag in released_buffers!"); | ||
| 151 | } | ||
| 144 | released_buffers.pop(); | 152 | released_buffers.pop(); |
| 145 | } | 153 | } |
| 146 | return tags; | 154 | 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 | |||
| 135 | math_util.h | 135 | math_util.h |
| 136 | memory_detect.cpp | 136 | memory_detect.cpp |
| 137 | memory_detect.h | 137 | memory_detect.h |
| 138 | memory_hook.cpp | ||
| 139 | memory_hook.h | ||
| 140 | microprofile.cpp | 138 | microprofile.cpp |
| 141 | microprofile.h | 139 | microprofile.h |
| 142 | microprofileui.h | 140 | microprofileui.h |
| @@ -162,6 +160,8 @@ add_library(common STATIC | |||
| 162 | thread.cpp | 160 | thread.cpp |
| 163 | thread.h | 161 | thread.h |
| 164 | thread_queue_list.h | 162 | thread_queue_list.h |
| 163 | thread_worker.cpp | ||
| 164 | thread_worker.h | ||
| 165 | threadsafe_queue.h | 165 | threadsafe_queue.h |
| 166 | time_zone.cpp | 166 | time_zone.cpp |
| 167 | time_zone.h | 167 | 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 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/memory_hook.h" | ||
| 6 | |||
| 7 | namespace Common { | ||
| 8 | |||
| 9 | MemoryHook::~MemoryHook() = default; | ||
| 10 | |||
| 11 | } // 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 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Memory hooks have two purposes: | ||
| 16 | * 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement | ||
| 17 | * texture forwarding and memory breakpoints for debugging. | ||
| 18 | * 2. To allow for the implementation of MMIO devices. | ||
| 19 | * | ||
| 20 | * A hook may be mapped to multiple regions of memory. | ||
| 21 | * | ||
| 22 | * If a std::nullopt or false is returned from a function, the read/write request is passed through | ||
| 23 | * to the underlying memory region. | ||
| 24 | */ | ||
| 25 | class MemoryHook { | ||
| 26 | public: | ||
| 27 | virtual ~MemoryHook(); | ||
| 28 | |||
| 29 | virtual std::optional<bool> IsValidAddress(VAddr addr) = 0; | ||
| 30 | |||
| 31 | virtual std::optional<u8> Read8(VAddr addr) = 0; | ||
| 32 | virtual std::optional<u16> Read16(VAddr addr) = 0; | ||
| 33 | virtual std::optional<u32> Read32(VAddr addr) = 0; | ||
| 34 | virtual std::optional<u64> Read64(VAddr addr) = 0; | ||
| 35 | |||
| 36 | virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; | ||
| 37 | |||
| 38 | virtual bool Write8(VAddr addr, u8 data) = 0; | ||
| 39 | virtual bool Write16(VAddr addr, u16 data) = 0; | ||
| 40 | virtual bool Write32(VAddr addr, u32 data) = 0; | ||
| 41 | virtual bool Write64(VAddr addr, u64 data) = 0; | ||
| 42 | |||
| 43 | virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0; | ||
| 44 | }; | ||
| 45 | |||
| 46 | using MemoryHookPointer = std::shared_ptr<MemoryHook>; | ||
| 47 | } // 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; | |||
| 10 | 10 | ||
| 11 | PageTable::~PageTable() noexcept = default; | 11 | PageTable::~PageTable() noexcept = default; |
| 12 | 12 | ||
| 13 | void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, | 13 | void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) { |
| 14 | bool has_attribute) { | 14 | const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)}; |
| 15 | const std::size_t num_page_table_entries{1ULL | ||
| 16 | << (address_space_width_in_bits - page_size_in_bits)}; | ||
| 17 | pointers.resize(num_page_table_entries); | 15 | pointers.resize(num_page_table_entries); |
| 18 | backing_addr.resize(num_page_table_entries); | 16 | backing_addr.resize(num_page_table_entries); |
| 19 | |||
| 20 | if (has_attribute) { | ||
| 21 | attributes.resize(num_page_table_entries); | ||
| 22 | } | ||
| 23 | } | 17 | } |
| 24 | 18 | ||
| 25 | } // namespace Common | 19 | } // 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 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <tuple> | 8 | #include <tuple> |
| 8 | 9 | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/memory_hook.h" | ||
| 11 | #include "common/virtual_buffer.h" | 11 | #include "common/virtual_buffer.h" |
| 12 | 12 | ||
| 13 | namespace Common { | 13 | namespace Common { |
| @@ -20,27 +20,6 @@ enum class PageType : u8 { | |||
| 20 | /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and | 20 | /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and |
| 21 | /// invalidation | 21 | /// invalidation |
| 22 | RasterizerCachedMemory, | 22 | RasterizerCachedMemory, |
| 23 | /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. | ||
| 24 | Special, | ||
| 25 | /// Page is allocated for use. | ||
| 26 | Allocated, | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct SpecialRegion { | ||
| 30 | enum class Type { | ||
| 31 | DebugHook, | ||
| 32 | IODevice, | ||
| 33 | } type; | ||
| 34 | |||
| 35 | MemoryHookPointer handler; | ||
| 36 | |||
| 37 | [[nodiscard]] bool operator<(const SpecialRegion& other) const { | ||
| 38 | return std::tie(type, handler) < std::tie(other.type, other.handler); | ||
| 39 | } | ||
| 40 | |||
| 41 | [[nodiscard]] bool operator==(const SpecialRegion& other) const { | ||
| 42 | return std::tie(type, handler) == std::tie(other.type, other.handler); | ||
| 43 | } | ||
| 44 | }; | 23 | }; |
| 45 | 24 | ||
| 46 | /** | 25 | /** |
| @@ -48,6 +27,59 @@ struct SpecialRegion { | |||
| 48 | * mimics the way a real CPU page table works. | 27 | * mimics the way a real CPU page table works. |
| 49 | */ | 28 | */ |
| 50 | struct PageTable { | 29 | struct PageTable { |
| 30 | /// Number of bits reserved for attribute tagging. | ||
| 31 | /// This can be at most the guaranteed alignment of the pointers in the page table. | ||
| 32 | static constexpr int ATTRIBUTE_BITS = 2; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Pair of host pointer and page type attribute. | ||
| 36 | * This uses the lower bits of a given pointer to store the attribute tag. | ||
| 37 | * Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method | ||
| 38 | * call. In other words, they are guaranteed to be synchronized at all times. | ||
| 39 | */ | ||
| 40 | class PageInfo { | ||
| 41 | public: | ||
| 42 | /// Returns the page pointer | ||
| 43 | [[nodiscard]] u8* Pointer() const noexcept { | ||
| 44 | return ExtractPointer(raw.load(std::memory_order_relaxed)); | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Returns the page type attribute | ||
| 48 | [[nodiscard]] PageType Type() const noexcept { | ||
| 49 | return ExtractType(raw.load(std::memory_order_relaxed)); | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Returns the page pointer and attribute pair, extracted from the same atomic read | ||
| 53 | [[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept { | ||
| 54 | const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed); | ||
| 55 | return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)}; | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Returns the raw representation of the page information. | ||
| 59 | /// Use ExtractPointer and ExtractType to unpack the value. | ||
| 60 | [[nodiscard]] uintptr_t Raw() const noexcept { | ||
| 61 | return raw.load(std::memory_order_relaxed); | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Write a page pointer and type pair atomically | ||
| 65 | void Store(u8* pointer, PageType type) noexcept { | ||
| 66 | raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type)); | ||
| 67 | } | ||
| 68 | |||
| 69 | /// Unpack a pointer from a page info raw representation | ||
| 70 | [[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept { | ||
| 71 | return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS)); | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Unpack a page type from a page info raw representation | ||
| 75 | [[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept { | ||
| 76 | return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1)); | ||
| 77 | } | ||
| 78 | |||
| 79 | private: | ||
| 80 | std::atomic<uintptr_t> raw; | ||
| 81 | }; | ||
| 82 | |||
| 51 | PageTable(); | 83 | PageTable(); |
| 52 | ~PageTable() noexcept; | 84 | ~PageTable() noexcept; |
| 53 | 85 | ||
| @@ -58,25 +90,21 @@ struct PageTable { | |||
| 58 | PageTable& operator=(PageTable&&) noexcept = default; | 90 | PageTable& operator=(PageTable&&) noexcept = default; |
| 59 | 91 | ||
| 60 | /** | 92 | /** |
| 61 | * Resizes the page table to be able to accomodate enough pages within | 93 | * Resizes the page table to be able to accommodate enough pages within |
| 62 | * a given address space. | 94 | * a given address space. |
| 63 | * | 95 | * |
| 64 | * @param address_space_width_in_bits The address size width in bits. | 96 | * @param address_space_width_in_bits The address size width in bits. |
| 65 | * @param page_size_in_bits The page size in bits. | 97 | * @param page_size_in_bits The page size in bits. |
| 66 | * @param has_attribute Whether or not this page has any backing attributes. | ||
| 67 | */ | 98 | */ |
| 68 | void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, | 99 | void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits); |
| 69 | bool has_attribute); | ||
| 70 | 100 | ||
| 71 | /** | 101 | /** |
| 72 | * Vector of memory pointers backing each page. An entry can only be non-null if the | 102 | * Vector of memory pointers backing each page. An entry can only be non-null if the |
| 73 | * corresponding entry in the `attributes` vector is of type `Memory`. | 103 | * corresponding attribute element is of type `Memory`. |
| 74 | */ | 104 | */ |
| 75 | VirtualBuffer<u8*> pointers; | 105 | VirtualBuffer<PageInfo> pointers; |
| 76 | 106 | ||
| 77 | VirtualBuffer<u64> backing_addr; | 107 | VirtualBuffer<u64> backing_addr; |
| 78 | |||
| 79 | VirtualBuffer<PageType> attributes; | ||
| 80 | }; | 108 | }; |
| 81 | 109 | ||
| 82 | } // namespace Common | 110 | } // 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: | |||
| 394 | template <typename S, typename T2, typename F2> | 394 | template <typename S, typename T2, typename F2> |
| 395 | friend S operator%(const S& p, const swapped_t v); | 395 | friend S operator%(const S& p, const swapped_t v); |
| 396 | 396 | ||
| 397 | // Arithmetics + assignements | 397 | // Arithmetics + assignments |
| 398 | template <typename S, typename T2, typename F2> | 398 | template <typename S, typename T2, typename F2> |
| 399 | friend S operator+=(const S& p, const swapped_t v); | 399 | friend S operator+=(const S& p, const swapped_t v); |
| 400 | 400 | ||
| @@ -451,7 +451,7 @@ S operator%(const S& i, const swap_struct_t<T, F> v) { | |||
| 451 | return i % v.swap(); | 451 | return i % v.swap(); |
| 452 | } | 452 | } |
| 453 | 453 | ||
| 454 | // Arithmetics + assignements | 454 | // Arithmetics + assignments |
| 455 | template <typename S, typename T, typename F> | 455 | template <typename S, typename T, typename F> |
| 456 | S& operator+=(S& i, const swap_struct_t<T, F> v) { | 456 | S& operator+=(S& i, const swap_struct_t<T, F> v) { |
| 457 | i += v.swap(); | 457 | 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 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/thread.h" | ||
| 6 | #include "common/thread_worker.h" | ||
| 7 | |||
| 8 | namespace Common { | ||
| 9 | |||
| 10 | ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) { | ||
| 11 | for (std::size_t i = 0; i < num_workers; ++i) | ||
| 12 | threads.emplace_back([this, thread_name{std::string{name}}] { | ||
| 13 | Common::SetCurrentThreadName(thread_name.c_str()); | ||
| 14 | |||
| 15 | // Wait for first request | ||
| 16 | { | ||
| 17 | std::unique_lock lock{queue_mutex}; | ||
| 18 | condition.wait(lock, [this] { return stop || !requests.empty(); }); | ||
| 19 | } | ||
| 20 | |||
| 21 | while (true) { | ||
| 22 | std::function<void()> task; | ||
| 23 | |||
| 24 | { | ||
| 25 | std::unique_lock lock{queue_mutex}; | ||
| 26 | condition.wait(lock, [this] { return stop || !requests.empty(); }); | ||
| 27 | if (stop || requests.empty()) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | task = std::move(requests.front()); | ||
| 31 | requests.pop(); | ||
| 32 | } | ||
| 33 | |||
| 34 | task(); | ||
| 35 | } | ||
| 36 | }); | ||
| 37 | } | ||
| 38 | |||
| 39 | ThreadWorker::~ThreadWorker() { | ||
| 40 | { | ||
| 41 | std::unique_lock lock{queue_mutex}; | ||
| 42 | stop = true; | ||
| 43 | } | ||
| 44 | condition.notify_all(); | ||
| 45 | for (std::thread& thread : threads) { | ||
| 46 | thread.join(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void ThreadWorker::QueueWork(std::function<void()>&& work) { | ||
| 51 | { | ||
| 52 | std::unique_lock lock{queue_mutex}; | ||
| 53 | requests.emplace(work); | ||
| 54 | } | ||
| 55 | condition.notify_one(); | ||
| 56 | } | ||
| 57 | |||
| 58 | } // 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 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <functional> | ||
| 9 | #include <mutex> | ||
| 10 | #include <string> | ||
| 11 | #include <vector> | ||
| 12 | #include <queue> | ||
| 13 | |||
| 14 | namespace Common { | ||
| 15 | |||
| 16 | class ThreadWorker final { | ||
| 17 | public: | ||
| 18 | explicit ThreadWorker(std::size_t num_workers, const std::string& name); | ||
| 19 | ~ThreadWorker(); | ||
| 20 | void QueueWork(std::function<void()>&& work); | ||
| 21 | |||
| 22 | private: | ||
| 23 | std::vector<std::thread> threads; | ||
| 24 | std::queue<std::function<void()>> requests; | ||
| 25 | std::mutex queue_mutex; | ||
| 26 | std::condition_variable condition; | ||
| 27 | std::atomic_bool stop{}; | ||
| 28 | }; | ||
| 29 | |||
| 30 | } // 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; | |||
| 15 | template <typename T> | 15 | template <typename T> |
| 16 | class VirtualBuffer final { | 16 | class VirtualBuffer final { |
| 17 | public: | 17 | public: |
| 18 | static_assert( | 18 | // TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible |
| 19 | std::is_trivially_constructible_v<T>, | 19 | // using std::atomic_ref once libc++ has support for it |
| 20 | "T must be trivially constructible, as non-trivial constructors will not be executed " | 20 | // static_assert( |
| 21 | "with the current allocator"); | 21 | // std::is_trivially_constructible_v<T>, |
| 22 | // "T must be trivially constructible, as non-trivial constructors will not be executed " | ||
| 23 | // "with the current allocator"); | ||
| 22 | 24 | ||
| 23 | constexpr VirtualBuffer() = default; | 25 | constexpr VirtualBuffer() = default; |
| 24 | explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { | 26 | 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 | |||
| 202 | hle/kernel/server_port.h | 202 | hle/kernel/server_port.h |
| 203 | hle/kernel/server_session.cpp | 203 | hle/kernel/server_session.cpp |
| 204 | hle/kernel/server_session.h | 204 | hle/kernel/server_session.h |
| 205 | hle/kernel/service_thread.cpp | ||
| 206 | hle/kernel/service_thread.h | ||
| 205 | hle/kernel/session.cpp | 207 | hle/kernel/session.cpp |
| 206 | hle/kernel/session.h | 208 | hle/kernel/session.h |
| 207 | hle/kernel/shared_memory.cpp | 209 | hle/kernel/shared_memory.cpp |
| @@ -500,7 +502,6 @@ add_library(core STATIC | |||
| 500 | hle/service/sm/controller.h | 502 | hle/service/sm/controller.h |
| 501 | hle/service/sm/sm.cpp | 503 | hle/service/sm/sm.cpp |
| 502 | hle/service/sm/sm.h | 504 | hle/service/sm/sm.h |
| 503 | hle/service/sockets/blocking_worker.h | ||
| 504 | hle/service/sockets/bsd.cpp | 505 | hle/service/sockets/bsd.cpp |
| 505 | hle/service/sockets/bsd.h | 506 | hle/service/sockets/bsd.h |
| 506 | hle/service/sockets/ethc.cpp | 507 | 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& | |||
| 133 | config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( | 133 | config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>( |
| 134 | page_table.pointers.data()); | 134 | page_table.pointers.data()); |
| 135 | config.absolute_offset_page_table = true; | 135 | config.absolute_offset_page_table = true; |
| 136 | config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; | ||
| 136 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; | 137 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; |
| 137 | config.only_detect_misalignment_via_page_table_on_page_boundary = true; | 138 | config.only_detect_misalignment_via_page_table_on_page_boundary = true; |
| 138 | 139 | ||
| @@ -180,6 +181,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& | |||
| 180 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { | 181 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { |
| 181 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; | 182 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; |
| 182 | } | 183 | } |
| 184 | if (Settings::values.cpuopt_unsafe_inaccurate_nan) { | ||
| 185 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; | ||
| 186 | } | ||
| 183 | } | 187 | } |
| 184 | 188 | ||
| 185 | return std::make_unique<Dynarmic::A32::Jit>(config); | 189 | 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& | |||
| 152 | // Memory | 152 | // Memory |
| 153 | config.page_table = reinterpret_cast<void**>(page_table.pointers.data()); | 153 | config.page_table = reinterpret_cast<void**>(page_table.pointers.data()); |
| 154 | config.page_table_address_space_bits = address_space_bits; | 154 | config.page_table_address_space_bits = address_space_bits; |
| 155 | config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS; | ||
| 155 | config.silently_mirror_page_table = false; | 156 | config.silently_mirror_page_table = false; |
| 156 | config.absolute_offset_page_table = true; | 157 | config.absolute_offset_page_table = true; |
| 157 | config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; | 158 | 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& | |||
| 211 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { | 212 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { |
| 212 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; | 213 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; |
| 213 | } | 214 | } |
| 215 | if (Settings::values.cpuopt_unsafe_inaccurate_nan) { | ||
| 216 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; | ||
| 217 | } | ||
| 214 | } | 218 | } |
| 215 | 219 | ||
| 216 | return std::make_shared<Dynarmic::A64::Jit>(config); | 220 | 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 { | |||
| 159 | device_memory = std::make_unique<Core::DeviceMemory>(); | 159 | device_memory = std::make_unique<Core::DeviceMemory>(); |
| 160 | 160 | ||
| 161 | is_multicore = Settings::values.use_multi_core.GetValue(); | 161 | is_multicore = Settings::values.use_multi_core.GetValue(); |
| 162 | is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); | 162 | is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); |
| 163 | 163 | ||
| 164 | kernel.SetMulticore(is_multicore); | 164 | kernel.SetMulticore(is_multicore); |
| 165 | cpu_manager.SetMulticore(is_multicore); | 165 | cpu_manager.SetMulticore(is_multicore); |
| @@ -307,7 +307,6 @@ struct System::Impl { | |||
| 307 | service_manager.reset(); | 307 | service_manager.reset(); |
| 308 | cheat_engine.reset(); | 308 | cheat_engine.reset(); |
| 309 | telemetry_session.reset(); | 309 | telemetry_session.reset(); |
| 310 | device_memory.reset(); | ||
| 311 | 310 | ||
| 312 | // Close all CPU/threading state | 311 | // Close all CPU/threading state |
| 313 | cpu_manager.Shutdown(); | 312 | 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( | |||
| 46 | boost::range::remove_erase(connected_sessions, server_session); | 46 | boost::range::remove_erase(connected_sessions, server_session); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( | ||
| 50 | const std::string& reason, u64 timeout, WakeupCallback&& callback, | ||
| 51 | std::shared_ptr<WritableEvent> writable_event) { | ||
| 52 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. | ||
| 53 | |||
| 54 | if (!writable_event) { | ||
| 55 | // Create event if not provided | ||
| 56 | const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason); | ||
| 57 | writable_event = pair.writable; | ||
| 58 | } | ||
| 59 | |||
| 60 | Handle event_handle = InvalidHandle; | ||
| 61 | { | ||
| 62 | KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout); | ||
| 63 | thread->SetHLECallback( | ||
| 64 | [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool { | ||
| 65 | ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT | ||
| 66 | ? ThreadWakeupReason::Timeout | ||
| 67 | : ThreadWakeupReason::Signal; | ||
| 68 | callback(thread, context, reason); | ||
| 69 | context.WriteToOutgoingCommandBuffer(*thread); | ||
| 70 | return true; | ||
| 71 | }); | ||
| 72 | const auto readable_event{writable_event->GetReadableEvent()}; | ||
| 73 | writable_event->Clear(); | ||
| 74 | thread->SetHLESyncObject(readable_event.get()); | ||
| 75 | thread->SetStatus(ThreadStatus::WaitHLEEvent); | ||
| 76 | thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 77 | readable_event->AddWaitingThread(thread); | ||
| 78 | } | ||
| 79 | thread->SetHLETimeEvent(event_handle); | ||
| 80 | |||
| 81 | is_thread_waiting = true; | ||
| 82 | |||
| 83 | return writable_event; | ||
| 84 | } | ||
| 85 | |||
| 86 | HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, | 49 | HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, |
| 87 | std::shared_ptr<ServerSession> server_session, | 50 | std::shared_ptr<ServerSession> server_session, |
| 88 | std::shared_ptr<Thread> thread) | 51 | 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: | |||
| 129 | using WakeupCallback = std::function<void( | 129 | using WakeupCallback = std::function<void( |
| 130 | std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>; | 130 | std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>; |
| 131 | 131 | ||
| 132 | /** | ||
| 133 | * Puts the specified guest thread to sleep until the returned event is signaled or until the | ||
| 134 | * specified timeout expires. | ||
| 135 | * @param reason Reason for pausing the thread, to be used for debugging purposes. | ||
| 136 | * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback | ||
| 137 | * invoked with a Timeout reason. | ||
| 138 | * @param callback Callback to be invoked when the thread is resumed. This callback must write | ||
| 139 | * the entire command response once again, regardless of the state of it before this function | ||
| 140 | * was called. | ||
| 141 | * @param writable_event Event to use to wake up the thread. If unspecified, an event will be | ||
| 142 | * created. | ||
| 143 | * @returns Event that when signaled will resume the thread and call the callback function. | ||
| 144 | */ | ||
| 145 | std::shared_ptr<WritableEvent> SleepClientThread( | ||
| 146 | const std::string& reason, u64 timeout, WakeupCallback&& callback, | ||
| 147 | std::shared_ptr<WritableEvent> writable_event = nullptr); | ||
| 148 | |||
| 149 | /// Populates this context with data from the requesting process/thread. | 132 | /// Populates this context with data from the requesting process/thread. |
| 150 | ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, | 133 | ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, |
| 151 | u32_le* src_cmdbuf); | 134 | 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 @@ | |||
| 8 | #include <functional> | 8 | #include <functional> |
| 9 | #include <memory> | 9 | #include <memory> |
| 10 | #include <thread> | 10 | #include <thread> |
| 11 | #include <unordered_map> | 11 | #include <unordered_set> |
| 12 | #include <utility> | 12 | #include <utility> |
| 13 | 13 | ||
| 14 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "common/microprofile.h" | 16 | #include "common/microprofile.h" |
| 17 | #include "common/thread.h" | 17 | #include "common/thread.h" |
| 18 | #include "common/thread_worker.h" | ||
| 18 | #include "core/arm/arm_interface.h" | 19 | #include "core/arm/arm_interface.h" |
| 19 | #include "core/arm/cpu_interrupt_handler.h" | 20 | #include "core/arm/cpu_interrupt_handler.h" |
| 20 | #include "core/arm/exclusive_monitor.h" | 21 | #include "core/arm/exclusive_monitor.h" |
| @@ -35,6 +36,7 @@ | |||
| 35 | #include "core/hle/kernel/physical_core.h" | 36 | #include "core/hle/kernel/physical_core.h" |
| 36 | #include "core/hle/kernel/process.h" | 37 | #include "core/hle/kernel/process.h" |
| 37 | #include "core/hle/kernel/resource_limit.h" | 38 | #include "core/hle/kernel/resource_limit.h" |
| 39 | #include "core/hle/kernel/service_thread.h" | ||
| 38 | #include "core/hle/kernel/shared_memory.h" | 40 | #include "core/hle/kernel/shared_memory.h" |
| 39 | #include "core/hle/kernel/synchronization.h" | 41 | #include "core/hle/kernel/synchronization.h" |
| 40 | #include "core/hle/kernel/thread.h" | 42 | #include "core/hle/kernel/thread.h" |
| @@ -60,6 +62,8 @@ struct KernelCore::Impl { | |||
| 60 | RegisterHostThread(); | 62 | RegisterHostThread(); |
| 61 | 63 | ||
| 62 | global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); | 64 | global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); |
| 65 | service_thread_manager = | ||
| 66 | std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager"); | ||
| 63 | 67 | ||
| 64 | InitializePhysicalCores(); | 68 | InitializePhysicalCores(); |
| 65 | InitializeSystemResourceLimit(kernel); | 69 | InitializeSystemResourceLimit(kernel); |
| @@ -76,6 +80,12 @@ struct KernelCore::Impl { | |||
| 76 | } | 80 | } |
| 77 | 81 | ||
| 78 | void Shutdown() { | 82 | void Shutdown() { |
| 83 | process_list.clear(); | ||
| 84 | |||
| 85 | // Ensures all service threads gracefully shutdown | ||
| 86 | service_thread_manager.reset(); | ||
| 87 | service_threads.clear(); | ||
| 88 | |||
| 79 | next_object_id = 0; | 89 | next_object_id = 0; |
| 80 | next_kernel_process_id = Process::InitialKIPIDMin; | 90 | next_kernel_process_id = Process::InitialKIPIDMin; |
| 81 | next_user_process_id = Process::ProcessIDMin; | 91 | next_user_process_id = Process::ProcessIDMin; |
| @@ -89,8 +99,6 @@ struct KernelCore::Impl { | |||
| 89 | 99 | ||
| 90 | cores.clear(); | 100 | cores.clear(); |
| 91 | 101 | ||
| 92 | process_list.clear(); | ||
| 93 | |||
| 94 | current_process = nullptr; | 102 | current_process = nullptr; |
| 95 | 103 | ||
| 96 | system_resource_limit = nullptr; | 104 | system_resource_limit = nullptr; |
| @@ -103,10 +111,8 @@ struct KernelCore::Impl { | |||
| 103 | 111 | ||
| 104 | exclusive_monitor.reset(); | 112 | exclusive_monitor.reset(); |
| 105 | 113 | ||
| 106 | num_host_threads = 0; | 114 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others |
| 107 | std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(), | 115 | next_host_thread_id = Core::Hardware::NUM_CPU_CORES; |
| 108 | std::thread::id{}); | ||
| 109 | std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0); | ||
| 110 | } | 116 | } |
| 111 | 117 | ||
| 112 | void InitializePhysicalCores() { | 118 | void InitializePhysicalCores() { |
| @@ -186,52 +192,46 @@ struct KernelCore::Impl { | |||
| 186 | } | 192 | } |
| 187 | } | 193 | } |
| 188 | 194 | ||
| 195 | /// Creates a new host thread ID, should only be called by GetHostThreadId | ||
| 196 | u32 AllocateHostThreadId(std::optional<std::size_t> core_id) { | ||
| 197 | if (core_id) { | ||
| 198 | // The first for slots are reserved for CPU core threads | ||
| 199 | ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 200 | return static_cast<u32>(*core_id); | ||
| 201 | } else { | ||
| 202 | return next_host_thread_id++; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Gets the host thread ID for the caller, allocating a new one if this is the first time | ||
| 207 | u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) { | ||
| 208 | const thread_local auto host_thread_id{AllocateHostThreadId(core_id)}; | ||
| 209 | return host_thread_id; | ||
| 210 | } | ||
| 211 | |||
| 212 | /// Registers a CPU core thread by allocating a host thread ID for it | ||
| 189 | void RegisterCoreThread(std::size_t core_id) { | 213 | void RegisterCoreThread(std::size_t core_id) { |
| 190 | const std::thread::id this_id = std::this_thread::get_id(); | 214 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); |
| 215 | const auto this_id = GetHostThreadId(core_id); | ||
| 191 | if (!is_multicore) { | 216 | if (!is_multicore) { |
| 192 | single_core_thread_id = this_id; | 217 | single_core_thread_id = this_id; |
| 193 | } | 218 | } |
| 194 | const auto end = | ||
| 195 | register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads); | ||
| 196 | const auto it = std::find(register_host_thread_keys.begin(), end, this_id); | ||
| 197 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 198 | ASSERT(it == end); | ||
| 199 | InsertHostThread(static_cast<u32>(core_id)); | ||
| 200 | } | 219 | } |
| 201 | 220 | ||
| 221 | /// Registers a new host thread by allocating a host thread ID for it | ||
| 202 | void RegisterHostThread() { | 222 | void RegisterHostThread() { |
| 203 | const std::thread::id this_id = std::this_thread::get_id(); | 223 | [[maybe_unused]] const auto this_id = GetHostThreadId(); |
| 204 | const auto end = | ||
| 205 | register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads); | ||
| 206 | const auto it = std::find(register_host_thread_keys.begin(), end, this_id); | ||
| 207 | if (it == end) { | ||
| 208 | InsertHostThread(registered_thread_ids++); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | void InsertHostThread(u32 value) { | ||
| 213 | const size_t index = num_host_threads++; | ||
| 214 | ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads"); | ||
| 215 | register_host_thread_values[index] = value; | ||
| 216 | register_host_thread_keys[index] = std::this_thread::get_id(); | ||
| 217 | } | 224 | } |
| 218 | 225 | ||
| 219 | [[nodiscard]] u32 GetCurrentHostThreadID() const { | 226 | [[nodiscard]] u32 GetCurrentHostThreadID() { |
| 220 | const std::thread::id this_id = std::this_thread::get_id(); | 227 | const auto this_id = GetHostThreadId(); |
| 221 | if (!is_multicore && single_core_thread_id == this_id) { | 228 | if (!is_multicore && single_core_thread_id == this_id) { |
| 222 | return static_cast<u32>(system.GetCpuManager().CurrentCore()); | 229 | return static_cast<u32>(system.GetCpuManager().CurrentCore()); |
| 223 | } | 230 | } |
| 224 | const auto end = | 231 | return this_id; |
| 225 | register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads); | ||
| 226 | const auto it = std::find(register_host_thread_keys.begin(), end, this_id); | ||
| 227 | if (it == end) { | ||
| 228 | return Core::INVALID_HOST_THREAD_ID; | ||
| 229 | } | ||
| 230 | return register_host_thread_values[static_cast<size_t>( | ||
| 231 | std::distance(register_host_thread_keys.begin(), it))]; | ||
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | Core::EmuThreadHandle GetCurrentEmuThreadID() const { | 234 | [[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() { |
| 235 | Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle(); | 235 | Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle(); |
| 236 | result.host_handle = GetCurrentHostThreadID(); | 236 | result.host_handle = GetCurrentHostThreadID(); |
| 237 | if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) { | 237 | if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) { |
| @@ -325,15 +325,8 @@ struct KernelCore::Impl { | |||
| 325 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; | 325 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; |
| 326 | std::vector<Kernel::PhysicalCore> cores; | 326 | std::vector<Kernel::PhysicalCore> cores; |
| 327 | 327 | ||
| 328 | // 0-3 IDs represent core threads, >3 represent others | 328 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others |
| 329 | std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; | 329 | std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES}; |
| 330 | |||
| 331 | // Number of host threads is a relatively high number to avoid overflowing | ||
| 332 | static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64; | ||
| 333 | std::atomic<size_t> num_host_threads{0}; | ||
| 334 | std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS> | ||
| 335 | register_host_thread_keys{}; | ||
| 336 | std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{}; | ||
| 337 | 330 | ||
| 338 | // Kernel memory management | 331 | // Kernel memory management |
| 339 | std::unique_ptr<Memory::MemoryManager> memory_manager; | 332 | std::unique_ptr<Memory::MemoryManager> memory_manager; |
| @@ -345,12 +338,19 @@ struct KernelCore::Impl { | |||
| 345 | std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; | 338 | std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; |
| 346 | std::shared_ptr<Kernel::SharedMemory> time_shared_mem; | 339 | std::shared_ptr<Kernel::SharedMemory> time_shared_mem; |
| 347 | 340 | ||
| 341 | // Threads used for services | ||
| 342 | std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; | ||
| 343 | |||
| 344 | // Service threads are managed by a worker thread, so that a calling service thread can queue up | ||
| 345 | // the release of itself | ||
| 346 | std::unique_ptr<Common::ThreadWorker> service_thread_manager; | ||
| 347 | |||
| 348 | std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; | 348 | std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; |
| 349 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; | 349 | std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; |
| 350 | std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; | 350 | std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; |
| 351 | 351 | ||
| 352 | bool is_multicore{}; | 352 | bool is_multicore{}; |
| 353 | std::thread::id single_core_thread_id{}; | 353 | u32 single_core_thread_id{}; |
| 354 | 354 | ||
| 355 | std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; | 355 | std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; |
| 356 | 356 | ||
| @@ -639,4 +639,19 @@ void KernelCore::ExitSVCProfile() { | |||
| 639 | MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); | 639 | MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); |
| 640 | } | 640 | } |
| 641 | 641 | ||
| 642 | std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { | ||
| 643 | auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name); | ||
| 644 | impl->service_thread_manager->QueueWork( | ||
| 645 | [this, service_thread] { impl->service_threads.emplace(service_thread); }); | ||
| 646 | return service_thread; | ||
| 647 | } | ||
| 648 | |||
| 649 | void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { | ||
| 650 | impl->service_thread_manager->QueueWork([this, service_thread] { | ||
| 651 | if (auto strong_ptr = service_thread.lock()) { | ||
| 652 | impl->service_threads.erase(strong_ptr); | ||
| 653 | } | ||
| 654 | }); | ||
| 655 | } | ||
| 656 | |||
| 642 | } // namespace Kernel | 657 | } // 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; | |||
| 42 | class ResourceLimit; | 42 | class ResourceLimit; |
| 43 | class KScheduler; | 43 | class KScheduler; |
| 44 | class SharedMemory; | 44 | class SharedMemory; |
| 45 | class ServiceThread; | ||
| 45 | class Synchronization; | 46 | class Synchronization; |
| 46 | class Thread; | 47 | class Thread; |
| 47 | class TimeManager; | 48 | class TimeManager; |
| @@ -227,6 +228,22 @@ public: | |||
| 227 | 228 | ||
| 228 | void ExitSVCProfile(); | 229 | void ExitSVCProfile(); |
| 229 | 230 | ||
| 231 | /** | ||
| 232 | * Creates an HLE service thread, which are used to execute service routines asynchronously. | ||
| 233 | * While these are allocated per ServerSession, these need to be owned and managed outside of | ||
| 234 | * ServerSession to avoid a circular dependency. | ||
| 235 | * @param name String name for the ServerSession creating this thread, used for debug purposes. | ||
| 236 | * @returns The a weak pointer newly created service thread. | ||
| 237 | */ | ||
| 238 | std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); | ||
| 239 | |||
| 240 | /** | ||
| 241 | * Releases a HLE service thread, instructing KernelCore to free it. This should be called when | ||
| 242 | * the ServerSession associated with the thread is destroyed. | ||
| 243 | * @param service_thread Service thread to release. | ||
| 244 | */ | ||
| 245 | void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread); | ||
| 246 | |||
| 230 | private: | 247 | private: |
| 231 | friend class Object; | 248 | friend class Object; |
| 232 | friend class Process; | 249 | 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 { | |||
| 73 | ThreadLocal = | 73 | ThreadLocal = |
| 74 | static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, | 74 | static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, |
| 75 | 75 | ||
| 76 | Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | | 76 | Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc | |
| 77 | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | | 77 | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | |
| 78 | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 78 | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| 79 | 79 | ||
| 80 | SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | | 80 | SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc | |
| 81 | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 81 | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| 82 | 82 | ||
| 83 | SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | | 83 | SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | |
| 84 | FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | 84 | FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, |
| @@ -111,8 +111,8 @@ static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09); | |||
| 111 | static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A); | 111 | static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A); |
| 112 | static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B); | 112 | static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B); |
| 113 | static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); | 113 | static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); |
| 114 | static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D); | 114 | static_assert(static_cast<u32>(MemoryState::Transferred) == 0x015C3C0D); |
| 115 | static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E); | 115 | static_assert(static_cast<u32>(MemoryState::SharedTransferred) == 0x005C380E); |
| 116 | static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F); | 116 | static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F); |
| 117 | static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010); | 117 | static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010); |
| 118 | static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811); | 118 | 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 | |||
| 265 | physical_memory_usage = 0; | 265 | physical_memory_usage = 0; |
| 266 | memory_pool = pool; | 266 | memory_pool = pool; |
| 267 | 267 | ||
| 268 | page_table_impl.Resize(address_space_width, PageBits, true); | 268 | page_table_impl.Resize(address_space_width, PageBits); |
| 269 | 269 | ||
| 270 | return InitializeMemoryLayout(start, end); | 270 | return InitializeMemoryLayout(start, end); |
| 271 | } | 271 | } |
| @@ -1007,8 +1007,8 @@ constexpr VAddr PageTable::GetRegionAddress(MemoryState state) const { | |||
| 1007 | case MemoryState::Shared: | 1007 | case MemoryState::Shared: |
| 1008 | case MemoryState::AliasCode: | 1008 | case MemoryState::AliasCode: |
| 1009 | case MemoryState::AliasCodeData: | 1009 | case MemoryState::AliasCodeData: |
| 1010 | case MemoryState::Transfered: | 1010 | case MemoryState::Transferred: |
| 1011 | case MemoryState::SharedTransfered: | 1011 | case MemoryState::SharedTransferred: |
| 1012 | case MemoryState::SharedCode: | 1012 | case MemoryState::SharedCode: |
| 1013 | case MemoryState::GeneratedCode: | 1013 | case MemoryState::GeneratedCode: |
| 1014 | case MemoryState::CodeOut: | 1014 | case MemoryState::CodeOut: |
| @@ -1042,8 +1042,8 @@ constexpr std::size_t PageTable::GetRegionSize(MemoryState state) const { | |||
| 1042 | case MemoryState::Shared: | 1042 | case MemoryState::Shared: |
| 1043 | case MemoryState::AliasCode: | 1043 | case MemoryState::AliasCode: |
| 1044 | case MemoryState::AliasCodeData: | 1044 | case MemoryState::AliasCodeData: |
| 1045 | case MemoryState::Transfered: | 1045 | case MemoryState::Transferred: |
| 1046 | case MemoryState::SharedTransfered: | 1046 | case MemoryState::SharedTransferred: |
| 1047 | case MemoryState::SharedCode: | 1047 | case MemoryState::SharedCode: |
| 1048 | case MemoryState::GeneratedCode: | 1048 | case MemoryState::GeneratedCode: |
| 1049 | case MemoryState::CodeOut: | 1049 | case MemoryState::CodeOut: |
| @@ -1080,8 +1080,8 @@ constexpr bool PageTable::CanContain(VAddr addr, std::size_t size, MemoryState s | |||
| 1080 | case MemoryState::AliasCodeData: | 1080 | case MemoryState::AliasCodeData: |
| 1081 | case MemoryState::Stack: | 1081 | case MemoryState::Stack: |
| 1082 | case MemoryState::ThreadLocal: | 1082 | case MemoryState::ThreadLocal: |
| 1083 | case MemoryState::Transfered: | 1083 | case MemoryState::Transferred: |
| 1084 | case MemoryState::SharedTransfered: | 1084 | case MemoryState::SharedTransferred: |
| 1085 | case MemoryState::SharedCode: | 1085 | case MemoryState::SharedCode: |
| 1086 | case MemoryState::GeneratedCode: | 1086 | case MemoryState::GeneratedCode: |
| 1087 | case MemoryState::CodeOut: | 1087 | 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 @@ | |||
| 25 | namespace Kernel { | 25 | namespace Kernel { |
| 26 | 26 | ||
| 27 | ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} | 27 | ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} |
| 28 | ServerSession::~ServerSession() = default; | 28 | |
| 29 | ServerSession::~ServerSession() { | ||
| 30 | kernel.ReleaseServiceThread(service_thread); | ||
| 31 | } | ||
| 29 | 32 | ||
| 30 | ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel, | 33 | ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel, |
| 31 | std::shared_ptr<Session> parent, | 34 | std::shared_ptr<Session> parent, |
| 32 | std::string name) { | 35 | std::string name) { |
| 33 | std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; | 36 | std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; |
| 34 | 37 | ||
| 35 | session->request_event = | ||
| 36 | Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) { | ||
| 37 | session->CompleteSyncRequest(); | ||
| 38 | }); | ||
| 39 | session->name = std::move(name); | 38 | session->name = std::move(name); |
| 40 | session->parent = std::move(parent); | 39 | session->parent = std::move(parent); |
| 40 | session->service_thread = kernel.CreateServiceThread(session->name); | ||
| 41 | 41 | ||
| 42 | return MakeResult(std::move(session)); | 42 | return MakeResult(std::move(session)); |
| 43 | } | 43 | } |
| @@ -142,16 +142,16 @@ ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, | |||
| 142 | std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread)); | 142 | std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread)); |
| 143 | 143 | ||
| 144 | context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); | 144 | context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); |
| 145 | request_queue.Push(std::move(context)); | 145 | |
| 146 | if (auto strong_ptr = service_thread.lock()) { | ||
| 147 | strong_ptr->QueueSyncRequest(*this, std::move(context)); | ||
| 148 | return RESULT_SUCCESS; | ||
| 149 | } | ||
| 146 | 150 | ||
| 147 | return RESULT_SUCCESS; | 151 | return RESULT_SUCCESS; |
| 148 | } | 152 | } |
| 149 | 153 | ||
| 150 | ResultCode ServerSession::CompleteSyncRequest() { | 154 | ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) { |
| 151 | ASSERT(!request_queue.Empty()); | ||
| 152 | |||
| 153 | auto& context = *request_queue.Front(); | ||
| 154 | |||
| 155 | ResultCode result = RESULT_SUCCESS; | 155 | ResultCode result = RESULT_SUCCESS; |
| 156 | // If the session has been converted to a domain, handle the domain request | 156 | // If the session has been converted to a domain, handle the domain request |
| 157 | if (IsDomain() && context.HasDomainMessageHeader()) { | 157 | if (IsDomain() && context.HasDomainMessageHeader()) { |
| @@ -177,18 +177,13 @@ ResultCode ServerSession::CompleteSyncRequest() { | |||
| 177 | } | 177 | } |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | request_queue.Pop(); | ||
| 181 | |||
| 182 | return result; | 180 | return result; |
| 183 | } | 181 | } |
| 184 | 182 | ||
| 185 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | 183 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, |
| 186 | Core::Memory::Memory& memory, | 184 | Core::Memory::Memory& memory, |
| 187 | Core::Timing::CoreTiming& core_timing) { | 185 | Core::Timing::CoreTiming& core_timing) { |
| 188 | const ResultCode result = QueueSyncRequest(std::move(thread), memory); | 186 | return QueueSyncRequest(std::move(thread), memory); |
| 189 | const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000}; | ||
| 190 | core_timing.ScheduleEvent(delay, request_event, {}); | ||
| 191 | return result; | ||
| 192 | } | 187 | } |
| 193 | 188 | ||
| 194 | } // namespace Kernel | 189 | } // 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 @@ | |||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | 11 | ||
| 12 | #include "common/threadsafe_queue.h" | 12 | #include "common/threadsafe_queue.h" |
| 13 | #include "core/hle/kernel/service_thread.h" | ||
| 13 | #include "core/hle/kernel/synchronization_object.h" | 14 | #include "core/hle/kernel/synchronization_object.h" |
| 14 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 15 | 16 | ||
| @@ -43,6 +44,8 @@ class Thread; | |||
| 43 | * TLS buffer and control is transferred back to it. | 44 | * TLS buffer and control is transferred back to it. |
| 44 | */ | 45 | */ |
| 45 | class ServerSession final : public SynchronizationObject { | 46 | class ServerSession final : public SynchronizationObject { |
| 47 | friend class ServiceThread; | ||
| 48 | |||
| 46 | public: | 49 | public: |
| 47 | explicit ServerSession(KernelCore& kernel); | 50 | explicit ServerSession(KernelCore& kernel); |
| 48 | ~ServerSession() override; | 51 | ~ServerSession() override; |
| @@ -132,7 +135,7 @@ private: | |||
| 132 | ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); | 135 | ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); |
| 133 | 136 | ||
| 134 | /// Completes a sync request from the emulated application. | 137 | /// Completes a sync request from the emulated application. |
| 135 | ResultCode CompleteSyncRequest(); | 138 | ResultCode CompleteSyncRequest(HLERequestContext& context); |
| 136 | 139 | ||
| 137 | /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an | 140 | /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an |
| 138 | /// object handle. | 141 | /// object handle. |
| @@ -163,11 +166,8 @@ private: | |||
| 163 | /// The name of this session (optional) | 166 | /// The name of this session (optional) |
| 164 | std::string name; | 167 | std::string name; |
| 165 | 168 | ||
| 166 | /// Core timing event used to schedule the service request at some point in the future | 169 | /// Thread to dispatch service requests |
| 167 | std::shared_ptr<Core::Timing::EventType> request_event; | 170 | std::weak_ptr<ServiceThread> service_thread; |
| 168 | |||
| 169 | /// Queue of scheduled service requests | ||
| 170 | Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue; | ||
| 171 | }; | 171 | }; |
| 172 | 172 | ||
| 173 | } // namespace Kernel | 173 | } // 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 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <condition_variable> | ||
| 6 | #include <functional> | ||
| 7 | #include <mutex> | ||
| 8 | #include <thread> | ||
| 9 | #include <vector> | ||
| 10 | #include <queue> | ||
| 11 | |||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/scope_exit.h" | ||
| 14 | #include "common/thread.h" | ||
| 15 | #include "core/core.h" | ||
| 16 | #include "core/hle/kernel/kernel.h" | ||
| 17 | #include "core/hle/kernel/server_session.h" | ||
| 18 | #include "core/hle/kernel/service_thread.h" | ||
| 19 | #include "core/hle/lock.h" | ||
| 20 | #include "video_core/renderer_base.h" | ||
| 21 | |||
| 22 | namespace Kernel { | ||
| 23 | |||
| 24 | class ServiceThread::Impl final { | ||
| 25 | public: | ||
| 26 | explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); | ||
| 27 | ~Impl(); | ||
| 28 | |||
| 29 | void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context); | ||
| 30 | |||
| 31 | private: | ||
| 32 | std::vector<std::thread> threads; | ||
| 33 | std::queue<std::function<void()>> requests; | ||
| 34 | std::mutex queue_mutex; | ||
| 35 | std::condition_variable condition; | ||
| 36 | const std::string service_name; | ||
| 37 | bool stop{}; | ||
| 38 | }; | ||
| 39 | |||
| 40 | ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name) | ||
| 41 | : service_name{name} { | ||
| 42 | for (std::size_t i = 0; i < num_threads; ++i) | ||
| 43 | threads.emplace_back([this, &kernel] { | ||
| 44 | Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str()); | ||
| 45 | |||
| 46 | // Wait for first request before trying to acquire a render context | ||
| 47 | { | ||
| 48 | std::unique_lock lock{queue_mutex}; | ||
| 49 | condition.wait(lock, [this] { return stop || !requests.empty(); }); | ||
| 50 | } | ||
| 51 | |||
| 52 | kernel.RegisterHostThread(); | ||
| 53 | |||
| 54 | while (true) { | ||
| 55 | std::function<void()> task; | ||
| 56 | |||
| 57 | { | ||
| 58 | std::unique_lock lock{queue_mutex}; | ||
| 59 | condition.wait(lock, [this] { return stop || !requests.empty(); }); | ||
| 60 | if (stop || requests.empty()) { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | task = std::move(requests.front()); | ||
| 64 | requests.pop(); | ||
| 65 | } | ||
| 66 | |||
| 67 | task(); | ||
| 68 | } | ||
| 69 | }); | ||
| 70 | } | ||
| 71 | |||
| 72 | void ServiceThread::Impl::QueueSyncRequest(ServerSession& session, | ||
| 73 | std::shared_ptr<HLERequestContext>&& context) { | ||
| 74 | { | ||
| 75 | std::unique_lock lock{queue_mutex}; | ||
| 76 | |||
| 77 | // ServerSession owns the service thread, so we cannot caption a strong pointer here in the | ||
| 78 | // event that the ServerSession is terminated. | ||
| 79 | std::weak_ptr<ServerSession> weak_ptr{SharedFrom(&session)}; | ||
| 80 | requests.emplace([weak_ptr, context{std::move(context)}]() { | ||
| 81 | if (auto strong_ptr = weak_ptr.lock()) { | ||
| 82 | strong_ptr->CompleteSyncRequest(*context); | ||
| 83 | } | ||
| 84 | }); | ||
| 85 | } | ||
| 86 | condition.notify_one(); | ||
| 87 | } | ||
| 88 | |||
| 89 | ServiceThread::Impl::~Impl() { | ||
| 90 | { | ||
| 91 | std::unique_lock lock{queue_mutex}; | ||
| 92 | stop = true; | ||
| 93 | } | ||
| 94 | condition.notify_all(); | ||
| 95 | for (std::thread& thread : threads) { | ||
| 96 | thread.join(); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) | ||
| 101 | : impl{std::make_unique<Impl>(kernel, num_threads, name)} {} | ||
| 102 | |||
| 103 | ServiceThread::~ServiceThread() = default; | ||
| 104 | |||
| 105 | void ServiceThread::QueueSyncRequest(ServerSession& session, | ||
| 106 | std::shared_ptr<HLERequestContext>&& context) { | ||
| 107 | impl->QueueSyncRequest(session, std::move(context)); | ||
| 108 | } | ||
| 109 | |||
| 110 | } // 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 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | |||
| 12 | class HLERequestContext; | ||
| 13 | class KernelCore; | ||
| 14 | class ServerSession; | ||
| 15 | |||
| 16 | class ServiceThread final { | ||
| 17 | public: | ||
| 18 | explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); | ||
| 19 | ~ServiceThread(); | ||
| 20 | |||
| 21 | void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context); | ||
| 22 | |||
| 23 | private: | ||
| 24 | class Impl; | ||
| 25 | std::unique_ptr<Impl> impl; | ||
| 26 | }; | ||
| 27 | |||
| 28 | } // 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 { | |||
| 23 | Ipc = 0x0A, | 23 | Ipc = 0x0A, |
| 24 | Stack = 0x0B, | 24 | Stack = 0x0B, |
| 25 | ThreadLocal = 0x0C, | 25 | ThreadLocal = 0x0C, |
| 26 | Transfered = 0x0D, | 26 | Transferred = 0x0D, |
| 27 | SharedTransfered = 0x0E, | 27 | SharedTransferred = 0x0E, |
| 28 | SharedCode = 0x0F, | 28 | SharedCode = 0x0F, |
| 29 | Inaccessible = 0x10, | 29 | Inaccessible = 0x10, |
| 30 | NonSecureIpc = 0x11, | 30 | 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 | |||
| 560 | 560 | ||
| 561 | AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { | 561 | AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { |
| 562 | on_new_message = | 562 | on_new_message = |
| 563 | Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageRecieved"); | 563 | Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageReceived"); |
| 564 | on_operation_mode_changed = | 564 | on_operation_mode_changed = |
| 565 | Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged"); | 565 | Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged"); |
| 566 | } | 566 | } |
| 567 | 567 | ||
| 568 | AppletMessageQueue::~AppletMessageQueue() = default; | 568 | AppletMessageQueue::~AppletMessageQueue() = default; |
| 569 | 569 | ||
| 570 | const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent() const { | 570 | const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMessageReceiveEvent() const { |
| 571 | return on_new_message.readable; | 571 | return on_new_message.readable; |
| 572 | } | 572 | } |
| 573 | 573 | ||
| @@ -675,7 +675,7 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { | |||
| 675 | 675 | ||
| 676 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 676 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 677 | rb.Push(RESULT_SUCCESS); | 677 | rb.Push(RESULT_SUCCESS); |
| 678 | rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent()); | 678 | rb.PushCopyObjects(msg_queue->GetMessageReceiveEvent()); |
| 679 | } | 679 | } |
| 680 | 680 | ||
| 681 | void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { | 681 | 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: | |||
| 55 | explicit AppletMessageQueue(Kernel::KernelCore& kernel); | 55 | explicit AppletMessageQueue(Kernel::KernelCore& kernel); |
| 56 | ~AppletMessageQueue(); | 56 | ~AppletMessageQueue(); |
| 57 | 57 | ||
| 58 | const std::shared_ptr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; | 58 | const std::shared_ptr<Kernel::ReadableEvent>& GetMessageReceiveEvent() const; |
| 59 | const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const; | 59 | const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const; |
| 60 | void PushMessage(AppletMessage msg); | 60 | void PushMessage(AppletMessage msg); |
| 61 | AppletMessage PopMessage(); | 61 | 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: | |||
| 70 | Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased"); | 70 | Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioOutBufferReleased"); |
| 71 | 71 | ||
| 72 | stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, | 72 | stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, |
| 73 | audio_params.channel_count, std::move(unique_name), | 73 | audio_params.channel_count, std::move(unique_name), [this] { |
| 74 | [this] { buffer_event.writable->Signal(); }); | 74 | const auto guard = LockService(); |
| 75 | buffer_event.writable->Signal(); | ||
| 76 | }); | ||
| 75 | } | 77 | } |
| 76 | 78 | ||
| 77 | private: | 79 | 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: | |||
| 49 | 49 | ||
| 50 | system_event = | 50 | system_event = |
| 51 | Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent"); | 51 | Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent"); |
| 52 | renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), system.Memory(), | 52 | renderer = std::make_unique<AudioCore::AudioRenderer>( |
| 53 | audren_params, system_event.writable, | 53 | system.CoreTiming(), system.Memory(), audren_params, |
| 54 | instance_number); | 54 | [this]() { |
| 55 | const auto guard = LockService(); | ||
| 56 | system_event.writable->Signal(); | ||
| 57 | }, | ||
| 58 | instance_number); | ||
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | private: | 61 | private: |
| 58 | void UpdateAudioCallback() { | ||
| 59 | system_event.writable->Signal(); | ||
| 60 | } | ||
| 61 | |||
| 62 | void GetSampleRate(Kernel::HLERequestContext& ctx) { | 62 | void GetSampleRate(Kernel::HLERequestContext& ctx) { |
| 63 | LOG_DEBUG(Service_Audio, "called"); | 63 | LOG_DEBUG(Service_Audio, "called"); |
| 64 | 64 | ||
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_) | |||
| 78 | pad_update_event = Core::Timing::CreateEvent( | 78 | pad_update_event = Core::Timing::CreateEvent( |
| 79 | "HID::UpdatePadCallback", | 79 | "HID::UpdatePadCallback", |
| 80 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 80 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 81 | const auto guard = LockService(); | ||
| 81 | UpdateControllers(user_data, ns_late); | 82 | UpdateControllers(user_data, ns_late); |
| 82 | }); | 83 | }); |
| 83 | motion_update_event = Core::Timing::CreateEvent( | 84 | motion_update_event = Core::Timing::CreateEvent( |
| 84 | "HID::MotionPadCallback", | 85 | "HID::MotionPadCallback", |
| 85 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | 86 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 87 | const auto guard = LockService(); | ||
| 86 | UpdateMotion(user_data, ns_late); | 88 | UpdateMotion(user_data, ns_late); |
| 87 | }); | 89 | }); |
| 88 | 90 | ||
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: | |||
| 31 | * @param output A buffer where the output data will be written to. | 31 | * @param output A buffer where the output data will be written to. |
| 32 | * @returns The result code of the ioctl. | 32 | * @returns The result code of the ioctl. |
| 33 | */ | 33 | */ |
| 34 | virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 34 | virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, |
| 35 | IoctlCtrl& ctrl) = 0; | 35 | std::vector<u8>& output) = 0; |
| 36 | 36 | ||
| 37 | /** | 37 | /** |
| 38 | * Handles an ioctl2 request. | 38 | * Handles an ioctl2 request. |
| @@ -43,8 +43,7 @@ public: | |||
| 43 | * @returns The result code of the ioctl. | 43 | * @returns The result code of the ioctl. |
| 44 | */ | 44 | */ |
| 45 | virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 45 | virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 46 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 46 | const std::vector<u8>& inline_input, std::vector<u8>& output) = 0; |
| 47 | IoctlCtrl& ctrl) = 0; | ||
| 48 | 47 | ||
| 49 | /** | 48 | /** |
| 50 | * Handles an ioctl3 request. | 49 | * Handles an ioctl3 request. |
| @@ -55,7 +54,7 @@ public: | |||
| 55 | * @returns The result code of the ioctl. | 54 | * @returns The result code of the ioctl. |
| 56 | */ | 55 | */ |
| 57 | virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 56 | virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 58 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) = 0; | 57 | std::vector<u8>& inline_output) = 0; |
| 59 | 58 | ||
| 60 | protected: | 59 | protected: |
| 61 | Core::System& system; | 60 | 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 | |||
| 18 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | 18 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} |
| 19 | nvdisp_disp0 ::~nvdisp_disp0() = default; | 19 | nvdisp_disp0 ::~nvdisp_disp0() = default; |
| 20 | 20 | ||
| 21 | NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 21 | NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input, |
| 22 | IoctlCtrl& ctrl) { | 22 | std::vector<u8>& output) { |
| 23 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 23 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 24 | return NvResult::NotImplemented; | 24 | return NvResult::NotImplemented; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input, | 27 | NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 28 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 28 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 29 | IoctlCtrl& ctrl) { | ||
| 30 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 29 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 31 | return NvResult::NotImplemented; | 30 | return NvResult::NotImplemented; |
| 32 | } | 31 | } |
| 33 | 32 | ||
| 34 | NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 33 | NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 35 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 34 | std::vector<u8>& inline_output) { |
| 36 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 35 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 37 | return NvResult::NotImplemented; | 36 | return NvResult::NotImplemented; |
| 38 | } | 37 | } |
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: | |||
| 20 | explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 20 | explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| 21 | ~nvdisp_disp0() override; | 21 | ~nvdisp_disp0() override; |
| 22 | 22 | ||
| 23 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 23 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 24 | IoctlCtrl& ctrl) override; | ||
| 25 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 24 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 26 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 25 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 27 | IoctlCtrl& ctrl) override; | ||
| 28 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 26 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 29 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 27 | std::vector<u8>& inline_output) override; |
| 30 | 28 | ||
| 31 | /// Performs a screen flip, drawing the buffer pointed to by the handle. | 29 | /// Performs a screen flip, drawing the buffer pointed to by the handle. |
| 32 | void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, | 30 | 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_ | |||
| 21 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | 21 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} |
| 22 | nvhost_as_gpu::~nvhost_as_gpu() = default; | 22 | nvhost_as_gpu::~nvhost_as_gpu() = default; |
| 23 | 23 | ||
| 24 | NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 24 | NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, |
| 25 | IoctlCtrl& ctrl) { | 25 | std::vector<u8>& output) { |
| 26 | switch (command.group) { | 26 | switch (command.group) { |
| 27 | case 'A': | 27 | case 'A': |
| 28 | switch (command.cmd) { | 28 | switch (command.cmd) { |
| @@ -55,14 +55,13 @@ NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std: | |||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, | 57 | NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 58 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 58 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 59 | IoctlCtrl& ctrl) { | ||
| 60 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 59 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 61 | return NvResult::NotImplemented; | 60 | return NvResult::NotImplemented; |
| 62 | } | 61 | } |
| 63 | 62 | ||
| 64 | NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 63 | NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 65 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 64 | std::vector<u8>& inline_output) { |
| 66 | switch (command.group) { | 65 | switch (command.group) { |
| 67 | case 'A': | 66 | case 'A': |
| 68 | switch (command.cmd) { | 67 | 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: | |||
| 30 | explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 30 | explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| 31 | ~nvhost_as_gpu() override; | 31 | ~nvhost_as_gpu() override; |
| 32 | 32 | ||
| 33 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 33 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 34 | IoctlCtrl& ctrl) override; | ||
| 35 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 34 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 36 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 35 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 37 | IoctlCtrl& ctrl) override; | ||
| 38 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 36 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 39 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 37 | std::vector<u8>& inline_output) override; |
| 40 | 38 | ||
| 41 | private: | 39 | private: |
| 42 | class BufferMap final { | 40 | 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, | |||
| 20 | : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {} | 20 | : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {} |
| 21 | nvhost_ctrl::~nvhost_ctrl() = default; | 21 | nvhost_ctrl::~nvhost_ctrl() = default; |
| 22 | 22 | ||
| 23 | NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 23 | NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 24 | IoctlCtrl& ctrl) { | ||
| 25 | switch (command.group) { | 24 | switch (command.group) { |
| 26 | case 0x0: | 25 | case 0x0: |
| 27 | switch (command.cmd) { | 26 | switch (command.cmd) { |
| @@ -30,9 +29,9 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v | |||
| 30 | case 0x1c: | 29 | case 0x1c: |
| 31 | return IocCtrlClearEventWait(input, output); | 30 | return IocCtrlClearEventWait(input, output); |
| 32 | case 0x1d: | 31 | case 0x1d: |
| 33 | return IocCtrlEventWait(input, output, false, ctrl); | 32 | return IocCtrlEventWait(input, output, false); |
| 34 | case 0x1e: | 33 | case 0x1e: |
| 35 | return IocCtrlEventWait(input, output, true, ctrl); | 34 | return IocCtrlEventWait(input, output, true); |
| 36 | case 0x1f: | 35 | case 0x1f: |
| 37 | return IocCtrlEventRegister(input, output); | 36 | return IocCtrlEventRegister(input, output); |
| 38 | case 0x20: | 37 | case 0x20: |
| @@ -48,14 +47,13 @@ NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::v | |||
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input, | 49 | NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 51 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 50 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 52 | IoctlCtrl& ctrl) { | ||
| 53 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 51 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 54 | return NvResult::NotImplemented; | 52 | return NvResult::NotImplemented; |
| 55 | } | 53 | } |
| 56 | 54 | ||
| 57 | NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 55 | NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 58 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 56 | std::vector<u8>& inline_outpu) { |
| 59 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 57 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 60 | return NvResult::NotImplemented; | 58 | return NvResult::NotImplemented; |
| 61 | } | 59 | } |
| @@ -69,7 +67,7 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector | |||
| 69 | } | 67 | } |
| 70 | 68 | ||
| 71 | NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, | 69 | NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, |
| 72 | bool is_async, IoctlCtrl& ctrl) { | 70 | bool is_async) { |
| 73 | IocCtrlEventWaitParams params{}; | 71 | IocCtrlEventWaitParams params{}; |
| 74 | std::memcpy(¶ms, input.data(), sizeof(params)); | 72 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 75 | LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", | 73 | 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 | |||
| 141 | params.value |= event_id; | 139 | params.value |= event_id; |
| 142 | event.event.writable->Clear(); | 140 | event.event.writable->Clear(); |
| 143 | gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); | 141 | gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); |
| 144 | if (!is_async && ctrl.fresh_call) { | ||
| 145 | ctrl.must_delay = true; | ||
| 146 | ctrl.timeout = params.timeout; | ||
| 147 | ctrl.event_id = event_id; | ||
| 148 | return NvResult::Timeout; | ||
| 149 | } | ||
| 150 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 142 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 151 | return NvResult::Timeout; | 143 | return NvResult::Timeout; |
| 152 | } | 144 | } |
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: | |||
| 18 | SyncpointManager& syncpoint_manager); | 18 | SyncpointManager& syncpoint_manager); |
| 19 | ~nvhost_ctrl() override; | 19 | ~nvhost_ctrl() override; |
| 20 | 20 | ||
| 21 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 21 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 22 | IoctlCtrl& ctrl) override; | ||
| 23 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 22 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 24 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 23 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 25 | IoctlCtrl& ctrl) override; | ||
| 26 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 24 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 27 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 25 | std::vector<u8>& inline_output) override; |
| 28 | 26 | ||
| 29 | private: | 27 | private: |
| 30 | struct IocSyncptReadParams { | 28 | struct IocSyncptReadParams { |
| @@ -123,8 +121,7 @@ private: | |||
| 123 | static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); | 121 | static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); |
| 124 | 122 | ||
| 125 | NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); | 123 | NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); |
| 126 | NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, | 124 | NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); |
| 127 | IoctlCtrl& ctrl); | ||
| 128 | NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); | 125 | NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); |
| 129 | NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); | 126 | NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); |
| 130 | NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); | 127 | 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) {} | |||
| 16 | nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; | 16 | nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; |
| 17 | 17 | ||
| 18 | NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, | 18 | NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, |
| 19 | std::vector<u8>& output, IoctlCtrl& ctrl) { | 19 | std::vector<u8>& output) { |
| 20 | switch (command.group) { | 20 | switch (command.group) { |
| 21 | case 'G': | 21 | case 'G': |
| 22 | switch (command.cmd) { | 22 | switch (command.cmd) { |
| @@ -48,15 +48,13 @@ NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, | 50 | NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 51 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 51 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 52 | IoctlCtrl& ctrl) { | ||
| 53 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 52 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 54 | return NvResult::NotImplemented; | 53 | return NvResult::NotImplemented; |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, | 56 | NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, |
| 58 | std::vector<u8>& output, std::vector<u8>& inline_output, | 57 | std::vector<u8>& output, std::vector<u8>& inline_output) { |
| 59 | IoctlCtrl& ctrl) { | ||
| 60 | switch (command.group) { | 58 | switch (command.group) { |
| 61 | case 'G': | 59 | case 'G': |
| 62 | switch (command.cmd) { | 60 | 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: | |||
| 16 | explicit nvhost_ctrl_gpu(Core::System& system); | 16 | explicit nvhost_ctrl_gpu(Core::System& system); |
| 17 | ~nvhost_ctrl_gpu() override; | 17 | ~nvhost_ctrl_gpu() override; |
| 18 | 18 | ||
| 19 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 19 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 20 | IoctlCtrl& ctrl) override; | ||
| 21 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 20 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 22 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 21 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 23 | IoctlCtrl& ctrl) override; | ||
| 24 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 22 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 25 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 23 | std::vector<u8>& inline_output) override; |
| 26 | 24 | ||
| 27 | private: | 25 | private: |
| 28 | struct IoctlGpuCharacteristics { | 26 | 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, | |||
| 23 | 23 | ||
| 24 | nvhost_gpu::~nvhost_gpu() = default; | 24 | nvhost_gpu::~nvhost_gpu() = default; |
| 25 | 25 | ||
| 26 | NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 26 | NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 27 | IoctlCtrl& ctrl) { | ||
| 28 | switch (command.group) { | 27 | switch (command.group) { |
| 29 | case 0x0: | 28 | case 0x0: |
| 30 | switch (command.cmd) { | 29 | switch (command.cmd) { |
| @@ -76,8 +75,7 @@ NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve | |||
| 76 | }; | 75 | }; |
| 77 | 76 | ||
| 78 | NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, | 77 | NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 79 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 78 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 80 | IoctlCtrl& ctrl) { | ||
| 81 | switch (command.group) { | 79 | switch (command.group) { |
| 82 | case 'H': | 80 | case 'H': |
| 83 | switch (command.cmd) { | 81 | switch (command.cmd) { |
| @@ -91,7 +89,7 @@ NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input, | |||
| 91 | } | 89 | } |
| 92 | 90 | ||
| 93 | NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 91 | NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 94 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 92 | std::vector<u8>& inline_output) { |
| 95 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 93 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 96 | return NvResult::NotImplemented; | 94 | return NvResult::NotImplemented; |
| 97 | } | 95 | } |
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: | |||
| 26 | SyncpointManager& syncpoint_manager); | 26 | SyncpointManager& syncpoint_manager); |
| 27 | ~nvhost_gpu() override; | 27 | ~nvhost_gpu() override; |
| 28 | 28 | ||
| 29 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 29 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 30 | IoctlCtrl& ctrl) override; | ||
| 31 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 30 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 32 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 31 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 33 | IoctlCtrl& ctrl) override; | ||
| 34 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 32 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 35 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 33 | std::vector<u8>& inline_output) override; |
| 36 | 34 | ||
| 37 | private: | 35 | private: |
| 38 | enum class CtxObjects : u32_le { | 36 | 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 | |||
| 15 | : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} | 15 | : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} |
| 16 | nvhost_nvdec::~nvhost_nvdec() = default; | 16 | nvhost_nvdec::~nvhost_nvdec() = default; |
| 17 | 17 | ||
| 18 | NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 18 | NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, |
| 19 | IoctlCtrl& ctrl) { | 19 | std::vector<u8>& output) { |
| 20 | switch (command.group) { | 20 | switch (command.group) { |
| 21 | case 0x0: | 21 | case 0x0: |
| 22 | switch (command.cmd) { | 22 | switch (command.cmd) { |
| @@ -58,14 +58,13 @@ NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, std:: | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input, | 60 | NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 61 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 61 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 62 | IoctlCtrl& ctrl) { | ||
| 63 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 62 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 64 | return NvResult::NotImplemented; | 63 | return NvResult::NotImplemented; |
| 65 | } | 64 | } |
| 66 | 65 | ||
| 67 | NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 66 | NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 68 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 67 | std::vector<u8>& inline_output) { |
| 69 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 68 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 70 | return NvResult::NotImplemented; | 69 | return NvResult::NotImplemented; |
| 71 | } | 70 | } |
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: | |||
| 14 | explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 14 | explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| 15 | ~nvhost_nvdec() override; | 15 | ~nvhost_nvdec() override; |
| 16 | 16 | ||
| 17 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 17 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 18 | IoctlCtrl& ctrl) override; | ||
| 19 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 18 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 20 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 19 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 21 | IoctlCtrl& ctrl) override; | ||
| 22 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 20 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 23 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 21 | std::vector<u8>& inline_output) override; |
| 24 | }; | 22 | }; |
| 25 | 23 | ||
| 26 | } // namespace Service::Nvidia::Devices | 24 | } // 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 { | |||
| 13 | nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} | 13 | nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} |
| 14 | nvhost_nvjpg::~nvhost_nvjpg() = default; | 14 | nvhost_nvjpg::~nvhost_nvjpg() = default; |
| 15 | 15 | ||
| 16 | NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 16 | NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, |
| 17 | IoctlCtrl& ctrl) { | 17 | std::vector<u8>& output) { |
| 18 | switch (command.group) { | 18 | switch (command.group) { |
| 19 | case 'H': | 19 | case 'H': |
| 20 | switch (command.cmd) { | 20 | switch (command.cmd) { |
| @@ -33,14 +33,13 @@ NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input, std:: | |||
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input, | 35 | NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 36 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 36 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 37 | IoctlCtrl& ctrl) { | ||
| 38 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 37 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 39 | return NvResult::NotImplemented; | 38 | return NvResult::NotImplemented; |
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 41 | NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 43 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 42 | std::vector<u8>& inline_output) { |
| 44 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 43 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 45 | return NvResult::NotImplemented; | 44 | return NvResult::NotImplemented; |
| 46 | } | 45 | } |
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: | |||
| 16 | explicit nvhost_nvjpg(Core::System& system); | 16 | explicit nvhost_nvjpg(Core::System& system); |
| 17 | ~nvhost_nvjpg() override; | 17 | ~nvhost_nvjpg() override; |
| 18 | 18 | ||
| 19 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 19 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 20 | IoctlCtrl& ctrl) override; | ||
| 21 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 20 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 22 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 21 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 23 | IoctlCtrl& ctrl) override; | ||
| 24 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 22 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 25 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 23 | std::vector<u8>& inline_output) override; |
| 26 | 24 | ||
| 27 | private: | 25 | private: |
| 28 | struct IoctlSetNvmapFD { | 26 | 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) | |||
| 15 | 15 | ||
| 16 | nvhost_vic::~nvhost_vic() = default; | 16 | nvhost_vic::~nvhost_vic() = default; |
| 17 | 17 | ||
| 18 | NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 18 | NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 19 | IoctlCtrl& ctrl) { | ||
| 20 | switch (command.group) { | 19 | switch (command.group) { |
| 21 | case 0x0: | 20 | case 0x0: |
| 22 | switch (command.cmd) { | 21 | switch (command.cmd) { |
| @@ -51,14 +50,13 @@ NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::ve | |||
| 51 | } | 50 | } |
| 52 | 51 | ||
| 53 | NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input, | 52 | NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 54 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 53 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 55 | IoctlCtrl& ctrl) { | ||
| 56 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 54 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 57 | return NvResult::NotImplemented; | 55 | return NvResult::NotImplemented; |
| 58 | } | 56 | } |
| 59 | 57 | ||
| 60 | NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 58 | NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 61 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 59 | std::vector<u8>& inline_output) { |
| 62 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 60 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 63 | return NvResult::NotImplemented; | 61 | return NvResult::NotImplemented; |
| 64 | } | 62 | } |
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: | |||
| 14 | explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 14 | explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| 15 | ~nvhost_vic(); | 15 | ~nvhost_vic(); |
| 16 | 16 | ||
| 17 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 17 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 18 | IoctlCtrl& ctrl) override; | ||
| 19 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 18 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 20 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 19 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 21 | IoctlCtrl& ctrl) override; | ||
| 22 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 20 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 23 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 21 | std::vector<u8>& inline_output) override; |
| 24 | }; | 22 | }; |
| 25 | } // namespace Service::Nvidia::Devices | 23 | } // 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) { | |||
| 19 | 19 | ||
| 20 | nvmap::~nvmap() = default; | 20 | nvmap::~nvmap() = default; |
| 21 | 21 | ||
| 22 | NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 22 | NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { |
| 23 | IoctlCtrl& ctrl) { | ||
| 24 | switch (command.group) { | 23 | switch (command.group) { |
| 25 | case 0x1: | 24 | case 0x1: |
| 26 | switch (command.cmd) { | 25 | switch (command.cmd) { |
| @@ -49,14 +48,13 @@ NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector< | |||
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input, | 50 | NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 52 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 51 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 53 | IoctlCtrl& ctrl) { | ||
| 54 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 52 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 55 | return NvResult::NotImplemented; | 53 | return NvResult::NotImplemented; |
| 56 | } | 54 | } |
| 57 | 55 | ||
| 58 | NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 56 | NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 59 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 57 | std::vector<u8>& inline_output) { |
| 60 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); | 58 | UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw); |
| 61 | return NvResult::NotImplemented; | 59 | return NvResult::NotImplemented; |
| 62 | } | 60 | } |
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: | |||
| 19 | explicit nvmap(Core::System& system); | 19 | explicit nvmap(Core::System& system); |
| 20 | ~nvmap() override; | 20 | ~nvmap() override; |
| 21 | 21 | ||
| 22 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 22 | NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; |
| 23 | IoctlCtrl& ctrl) override; | ||
| 24 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, | 23 | NvResult Ioctl2(Ioctl command, const std::vector<u8>& input, |
| 25 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 24 | const std::vector<u8>& inline_input, std::vector<u8>& output) override; |
| 26 | IoctlCtrl& ctrl) override; | ||
| 27 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, | 25 | NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, |
| 28 | std::vector<u8>& inline_output, IoctlCtrl& ctrl) override; | 26 | std::vector<u8>& inline_output) override; |
| 29 | 27 | ||
| 30 | /// Returns the allocated address of an nvmap object given its handle. | 28 | /// Returns the allocated address of an nvmap object given its handle. |
| 31 | VAddr GetObjectAddress(u32 handle) const; | 29 | 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) { | |||
| 61 | std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); | 61 | std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); |
| 62 | const auto input_buffer = ctx.ReadBuffer(0); | 62 | const auto input_buffer = ctx.ReadBuffer(0); |
| 63 | 63 | ||
| 64 | IoctlCtrl ctrl{}; | 64 | const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); |
| 65 | 65 | if (command.is_out != 0) { | |
| 66 | const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer, ctrl); | 66 | ctx.WriteBuffer(output_buffer); |
| 67 | if (ctrl.must_delay) { | ||
| 68 | ctrl.fresh_call = false; | ||
| 69 | ctx.SleepClientThread( | ||
| 70 | "NVServices::DelayedResponse", ctrl.timeout, | ||
| 71 | [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, | ||
| 72 | Kernel::ThreadWakeupReason reason) { | ||
| 73 | IoctlCtrl ctrl2{ctrl}; | ||
| 74 | std::vector<u8> tmp_output = output_buffer; | ||
| 75 | const auto nv_result2 = nvdrv->Ioctl1(fd, command, input_buffer, tmp_output, ctrl2); | ||
| 76 | |||
| 77 | if (command.is_out != 0) { | ||
| 78 | ctx.WriteBuffer(tmp_output); | ||
| 79 | } | ||
| 80 | |||
| 81 | IPC::ResponseBuilder rb{ctx_, 3}; | ||
| 82 | rb.Push(RESULT_SUCCESS); | ||
| 83 | rb.PushEnum(nv_result2); | ||
| 84 | }, | ||
| 85 | nvdrv->GetEventWriteable(ctrl.event_id)); | ||
| 86 | } else { | ||
| 87 | if (command.is_out != 0) { | ||
| 88 | ctx.WriteBuffer(output_buffer); | ||
| 89 | } | ||
| 90 | } | 67 | } |
| 91 | 68 | ||
| 92 | IPC::ResponseBuilder rb{ctx, 3}; | 69 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -110,36 +87,8 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { | |||
| 110 | const auto input_inlined_buffer = ctx.ReadBuffer(1); | 87 | const auto input_inlined_buffer = ctx.ReadBuffer(1); |
| 111 | std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); | 88 | std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); |
| 112 | 89 | ||
| 113 | IoctlCtrl ctrl{}; | ||
| 114 | |||
| 115 | const auto nv_result = | 90 | const auto nv_result = |
| 116 | nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer, ctrl); | 91 | nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); |
| 117 | if (ctrl.must_delay) { | ||
| 118 | ctrl.fresh_call = false; | ||
| 119 | ctx.SleepClientThread( | ||
| 120 | "NVServices::DelayedResponse", ctrl.timeout, | ||
| 121 | [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, | ||
| 122 | Kernel::ThreadWakeupReason reason) { | ||
| 123 | IoctlCtrl ctrl2{ctrl}; | ||
| 124 | std::vector<u8> tmp_output = output_buffer; | ||
| 125 | const auto nv_result2 = nvdrv->Ioctl2(fd, command, input_buffer, | ||
| 126 | input_inlined_buffer, tmp_output, ctrl2); | ||
| 127 | |||
| 128 | if (command.is_out != 0) { | ||
| 129 | ctx.WriteBuffer(tmp_output); | ||
| 130 | } | ||
| 131 | |||
| 132 | IPC::ResponseBuilder rb{ctx_, 3}; | ||
| 133 | rb.Push(RESULT_SUCCESS); | ||
| 134 | rb.PushEnum(nv_result2); | ||
| 135 | }, | ||
| 136 | nvdrv->GetEventWriteable(ctrl.event_id)); | ||
| 137 | } else { | ||
| 138 | if (command.is_out != 0) { | ||
| 139 | ctx.WriteBuffer(output_buffer); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | if (command.is_out != 0) { | 92 | if (command.is_out != 0) { |
| 144 | ctx.WriteBuffer(output_buffer); | 93 | ctx.WriteBuffer(output_buffer); |
| 145 | } | 94 | } |
| @@ -165,36 +114,11 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { | |||
| 165 | std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); | 114 | std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); |
| 166 | std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); | 115 | std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); |
| 167 | 116 | ||
| 168 | IoctlCtrl ctrl{}; | ||
| 169 | const auto nv_result = | 117 | const auto nv_result = |
| 170 | nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline, ctrl); | 118 | nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline); |
| 171 | if (ctrl.must_delay) { | 119 | if (command.is_out != 0) { |
| 172 | ctrl.fresh_call = false; | 120 | ctx.WriteBuffer(output_buffer, 0); |
| 173 | ctx.SleepClientThread( | 121 | ctx.WriteBuffer(output_buffer_inline, 1); |
| 174 | "NVServices::DelayedResponse", ctrl.timeout, | ||
| 175 | [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, | ||
| 176 | Kernel::ThreadWakeupReason reason) { | ||
| 177 | IoctlCtrl ctrl2{ctrl}; | ||
| 178 | std::vector<u8> tmp_output = output_buffer; | ||
| 179 | std::vector<u8> tmp_output2 = output_buffer; | ||
| 180 | const auto nv_result2 = | ||
| 181 | nvdrv->Ioctl3(fd, command, input_buffer, tmp_output, tmp_output2, ctrl2); | ||
| 182 | |||
| 183 | if (command.is_out != 0) { | ||
| 184 | ctx.WriteBuffer(tmp_output, 0); | ||
| 185 | ctx.WriteBuffer(tmp_output2, 1); | ||
| 186 | } | ||
| 187 | |||
| 188 | IPC::ResponseBuilder rb{ctx_, 3}; | ||
| 189 | rb.Push(RESULT_SUCCESS); | ||
| 190 | rb.PushEnum(nv_result2); | ||
| 191 | }, | ||
| 192 | nvdrv->GetEventWriteable(ctrl.event_id)); | ||
| 193 | } else { | ||
| 194 | if (command.is_out != 0) { | ||
| 195 | ctx.WriteBuffer(output_buffer, 0); | ||
| 196 | ctx.WriteBuffer(output_buffer_inline, 1); | ||
| 197 | } | ||
| 198 | } | 122 | } |
| 199 | 123 | ||
| 200 | IPC::ResponseBuilder rb{ctx, 3}; | 124 | 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 { | |||
| 97 | BitField<31, 1, u32> is_out; | 97 | BitField<31, 1, u32> is_out; |
| 98 | }; | 98 | }; |
| 99 | 99 | ||
| 100 | struct IoctlCtrl { | ||
| 101 | // First call done to the servioce for services that call itself again after a call. | ||
| 102 | bool fresh_call{true}; | ||
| 103 | // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep | ||
| 104 | bool must_delay{}; | ||
| 105 | // Timeout for the delay | ||
| 106 | s64 timeout{}; | ||
| 107 | // NV Event Id | ||
| 108 | s32 event_id{-1}; | ||
| 109 | }; | ||
| 110 | |||
| 111 | } // namespace Service::Nvidia | 100 | } // 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) { | |||
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 93 | NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 94 | std::vector<u8>& output, IoctlCtrl& ctrl) { | 94 | std::vector<u8>& output) { |
| 95 | if (fd < 0) { | 95 | if (fd < 0) { |
| 96 | LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); | 96 | LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); |
| 97 | return NvResult::InvalidState; | 97 | return NvResult::InvalidState; |
| @@ -104,12 +104,11 @@ NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input | |||
| 104 | return NvResult::NotImplemented; | 104 | return NvResult::NotImplemented; |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | return itr->second->Ioctl1(command, input, output, ctrl); | 107 | return itr->second->Ioctl1(command, input, output); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 110 | NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 111 | const std::vector<u8>& inline_input, std::vector<u8>& output, | 111 | const std::vector<u8>& inline_input, std::vector<u8>& output) { |
| 112 | IoctlCtrl& ctrl) { | ||
| 113 | if (fd < 0) { | 112 | if (fd < 0) { |
| 114 | LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); | 113 | LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); |
| 115 | return NvResult::InvalidState; | 114 | return NvResult::InvalidState; |
| @@ -122,11 +121,11 @@ NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input | |||
| 122 | return NvResult::NotImplemented; | 121 | return NvResult::NotImplemented; |
| 123 | } | 122 | } |
| 124 | 123 | ||
| 125 | return itr->second->Ioctl2(command, input, inline_input, output, ctrl); | 124 | return itr->second->Ioctl2(command, input, inline_input, output); |
| 126 | } | 125 | } |
| 127 | 126 | ||
| 128 | NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 127 | NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 129 | std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl) { | 128 | std::vector<u8>& output, std::vector<u8>& inline_output) { |
| 130 | if (fd < 0) { | 129 | if (fd < 0) { |
| 131 | LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); | 130 | LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); |
| 132 | return NvResult::InvalidState; | 131 | return NvResult::InvalidState; |
| @@ -139,7 +138,7 @@ NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input | |||
| 139 | return NvResult::NotImplemented; | 138 | return NvResult::NotImplemented; |
| 140 | } | 139 | } |
| 141 | 140 | ||
| 142 | return itr->second->Ioctl3(command, input, output, inline_output, ctrl); | 141 | return itr->second->Ioctl3(command, input, output, inline_output); |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 145 | NvResult Module::Close(DeviceFD fd) { | 144 | 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: | |||
| 119 | 119 | ||
| 120 | /// Sends an ioctl command to the specified file descriptor. | 120 | /// Sends an ioctl command to the specified file descriptor. |
| 121 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 121 | NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 122 | std::vector<u8>& output, IoctlCtrl& ctrl); | 122 | std::vector<u8>& output); |
| 123 | 123 | ||
| 124 | NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 124 | NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 125 | const std::vector<u8>& inline_input, std::vector<u8>& output, IoctlCtrl& ctrl); | 125 | const std::vector<u8>& inline_input, std::vector<u8>& output); |
| 126 | 126 | ||
| 127 | NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 127 | NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| 128 | std::vector<u8>& output, std::vector<u8>& inline_output, IoctlCtrl& ctrl); | 128 | std::vector<u8>& output, std::vector<u8>& inline_output); |
| 129 | 129 | ||
| 130 | /// Closes a device file descriptor and returns operation success. | 130 | /// Closes a device file descriptor and returns operation success. |
| 131 | NvResult Close(DeviceFD fd); | 131 | 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) | |||
| 25 | ASSERT(slot < buffer_slots); | 25 | ASSERT(slot < buffer_slots); |
| 26 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); | 26 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); |
| 27 | 27 | ||
| 28 | free_buffers.push_back(slot); | 28 | { |
| 29 | std::unique_lock lock{queue_mutex}; | ||
| 30 | free_buffers.push_back(slot); | ||
| 31 | } | ||
| 32 | condition.notify_one(); | ||
| 33 | |||
| 29 | buffers[slot] = { | 34 | buffers[slot] = { |
| 30 | .slot = slot, | 35 | .slot = slot, |
| 31 | .status = Buffer::Status::Free, | 36 | .status = Buffer::Status::Free, |
| @@ -41,10 +46,20 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) | |||
| 41 | 46 | ||
| 42 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, | 47 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, |
| 43 | u32 height) { | 48 | u32 height) { |
| 49 | // Wait for first request before trying to dequeue | ||
| 50 | { | ||
| 51 | std::unique_lock lock{queue_mutex}; | ||
| 52 | condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); | ||
| 53 | } | ||
| 44 | 54 | ||
| 45 | if (free_buffers.empty()) { | 55 | if (!is_connect) { |
| 56 | // Buffer was disconnected while the thread was blocked, this is most likely due to | ||
| 57 | // emulation being stopped | ||
| 46 | return std::nullopt; | 58 | return std::nullopt; |
| 47 | } | 59 | } |
| 60 | |||
| 61 | std::unique_lock lock{queue_mutex}; | ||
| 62 | |||
| 48 | auto f_itr = free_buffers.begin(); | 63 | auto f_itr = free_buffers.begin(); |
| 49 | auto slot = buffers.size(); | 64 | auto slot = buffers.size(); |
| 50 | 65 | ||
| @@ -97,7 +112,11 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult | |||
| 97 | buffers[slot].multi_fence = multi_fence; | 112 | buffers[slot].multi_fence = multi_fence; |
| 98 | buffers[slot].swap_interval = 0; | 113 | buffers[slot].swap_interval = 0; |
| 99 | 114 | ||
| 100 | free_buffers.push_back(slot); | 115 | { |
| 116 | std::unique_lock lock{queue_mutex}; | ||
| 117 | free_buffers.push_back(slot); | ||
| 118 | } | ||
| 119 | condition.notify_one(); | ||
| 101 | 120 | ||
| 102 | buffer_wait_event.writable->Signal(); | 121 | buffer_wait_event.writable->Signal(); |
| 103 | } | 122 | } |
| @@ -127,15 +146,26 @@ void BufferQueue::ReleaseBuffer(u32 slot) { | |||
| 127 | ASSERT(buffers[slot].slot == slot); | 146 | ASSERT(buffers[slot].slot == slot); |
| 128 | 147 | ||
| 129 | buffers[slot].status = Buffer::Status::Free; | 148 | buffers[slot].status = Buffer::Status::Free; |
| 130 | free_buffers.push_back(slot); | 149 | { |
| 150 | std::unique_lock lock{queue_mutex}; | ||
| 151 | free_buffers.push_back(slot); | ||
| 152 | } | ||
| 153 | condition.notify_one(); | ||
| 131 | 154 | ||
| 132 | buffer_wait_event.writable->Signal(); | 155 | buffer_wait_event.writable->Signal(); |
| 133 | } | 156 | } |
| 134 | 157 | ||
| 158 | void BufferQueue::Connect() { | ||
| 159 | queue_sequence.clear(); | ||
| 160 | is_connect = true; | ||
| 161 | } | ||
| 162 | |||
| 135 | void BufferQueue::Disconnect() { | 163 | void BufferQueue::Disconnect() { |
| 136 | buffers.fill({}); | 164 | buffers.fill({}); |
| 137 | queue_sequence.clear(); | 165 | queue_sequence.clear(); |
| 138 | buffer_wait_event.writable->Signal(); | 166 | buffer_wait_event.writable->Signal(); |
| 167 | is_connect = false; | ||
| 168 | condition.notify_one(); | ||
| 139 | } | 169 | } |
| 140 | 170 | ||
| 141 | u32 BufferQueue::Query(QueryType type) { | 171 | 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 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <condition_variable> | ||
| 7 | #include <list> | 8 | #include <list> |
| 9 | #include <mutex> | ||
| 8 | #include <optional> | 10 | #include <optional> |
| 9 | #include <vector> | 11 | #include <vector> |
| 10 | 12 | ||
| @@ -99,6 +101,7 @@ public: | |||
| 99 | void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence); | 101 | void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence); |
| 100 | std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); | 102 | std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); |
| 101 | void ReleaseBuffer(u32 slot); | 103 | void ReleaseBuffer(u32 slot); |
| 104 | void Connect(); | ||
| 102 | void Disconnect(); | 105 | void Disconnect(); |
| 103 | u32 Query(QueryType type); | 106 | u32 Query(QueryType type); |
| 104 | 107 | ||
| @@ -106,18 +109,28 @@ public: | |||
| 106 | return id; | 109 | return id; |
| 107 | } | 110 | } |
| 108 | 111 | ||
| 112 | bool IsConnected() const { | ||
| 113 | return is_connect; | ||
| 114 | } | ||
| 115 | |||
| 109 | std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const; | 116 | std::shared_ptr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const; |
| 110 | 117 | ||
| 111 | std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const; | 118 | std::shared_ptr<Kernel::ReadableEvent> GetBufferWaitEvent() const; |
| 112 | 119 | ||
| 113 | private: | 120 | private: |
| 114 | u32 id; | 121 | BufferQueue(const BufferQueue&) = delete; |
| 115 | u64 layer_id; | 122 | |
| 123 | u32 id{}; | ||
| 124 | u64 layer_id{}; | ||
| 125 | std::atomic_bool is_connect{}; | ||
| 116 | 126 | ||
| 117 | std::list<u32> free_buffers; | 127 | std::list<u32> free_buffers; |
| 118 | std::array<Buffer, buffer_slots> buffers; | 128 | std::array<Buffer, buffer_slots> buffers; |
| 119 | std::list<u32> queue_sequence; | 129 | std::list<u32> queue_sequence; |
| 120 | Kernel::EventPair buffer_wait_event; | 130 | Kernel::EventPair buffer_wait_event; |
| 131 | |||
| 132 | std::mutex queue_mutex; | ||
| 133 | std::condition_variable condition; | ||
| 121 | }; | 134 | }; |
| 122 | 135 | ||
| 123 | } // namespace Service::NVFlinger | 136 | } // 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) { | |||
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | NVFlinger::~NVFlinger() { | 90 | NVFlinger::~NVFlinger() { |
| 91 | for (auto& buffer_queue : buffer_queues) { | ||
| 92 | buffer_queue->Disconnect(); | ||
| 93 | } | ||
| 94 | |||
| 91 | if (system.IsMulticore()) { | 95 | if (system.IsMulticore()) { |
| 92 | is_running = false; | 96 | is_running = false; |
| 93 | wait_event->Set(); | 97 | wait_event->Set(); |
| @@ -104,6 +108,8 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | |||
| 104 | } | 108 | } |
| 105 | 109 | ||
| 106 | std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { | 110 | std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { |
| 111 | const auto guard = Lock(); | ||
| 112 | |||
| 107 | LOG_DEBUG(Service, "Opening \"{}\" display", name); | 113 | LOG_DEBUG(Service, "Opening \"{}\" display", name); |
| 108 | 114 | ||
| 109 | // TODO(Subv): Currently we only support the Default display. | 115 | // TODO(Subv): Currently we only support the Default display. |
| @@ -121,6 +127,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { | |||
| 121 | } | 127 | } |
| 122 | 128 | ||
| 123 | std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | 129 | std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { |
| 130 | const auto guard = Lock(); | ||
| 124 | auto* const display = FindDisplay(display_id); | 131 | auto* const display = FindDisplay(display_id); |
| 125 | 132 | ||
| 126 | if (display == nullptr) { | 133 | if (display == nullptr) { |
| @@ -129,18 +136,22 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | |||
| 129 | 136 | ||
| 130 | const u64 layer_id = next_layer_id++; | 137 | const u64 layer_id = next_layer_id++; |
| 131 | const u32 buffer_queue_id = next_buffer_queue_id++; | 138 | const u32 buffer_queue_id = next_buffer_queue_id++; |
| 132 | buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id); | 139 | buffer_queues.emplace_back( |
| 133 | display->CreateLayer(layer_id, buffer_queues.back()); | 140 | std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id)); |
| 141 | display->CreateLayer(layer_id, *buffer_queues.back()); | ||
| 134 | return layer_id; | 142 | return layer_id; |
| 135 | } | 143 | } |
| 136 | 144 | ||
| 137 | void NVFlinger::CloseLayer(u64 layer_id) { | 145 | void NVFlinger::CloseLayer(u64 layer_id) { |
| 146 | const auto guard = Lock(); | ||
| 147 | |||
| 138 | for (auto& display : displays) { | 148 | for (auto& display : displays) { |
| 139 | display.CloseLayer(layer_id); | 149 | display.CloseLayer(layer_id); |
| 140 | } | 150 | } |
| 141 | } | 151 | } |
| 142 | 152 | ||
| 143 | std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const { | 153 | std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const { |
| 154 | const auto guard = Lock(); | ||
| 144 | const auto* const layer = FindLayer(display_id, layer_id); | 155 | const auto* const layer = FindLayer(display_id, layer_id); |
| 145 | 156 | ||
| 146 | if (layer == nullptr) { | 157 | if (layer == nullptr) { |
| @@ -151,6 +162,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co | |||
| 151 | } | 162 | } |
| 152 | 163 | ||
| 153 | std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { | 164 | std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { |
| 165 | const auto guard = Lock(); | ||
| 154 | auto* const display = FindDisplay(display_id); | 166 | auto* const display = FindDisplay(display_id); |
| 155 | 167 | ||
| 156 | if (display == nullptr) { | 168 | if (display == nullptr) { |
| @@ -160,20 +172,16 @@ std::shared_ptr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) | |||
| 160 | return display->GetVSyncEvent(); | 172 | return display->GetVSyncEvent(); |
| 161 | } | 173 | } |
| 162 | 174 | ||
| 163 | BufferQueue& NVFlinger::FindBufferQueue(u32 id) { | 175 | BufferQueue* NVFlinger::FindBufferQueue(u32 id) { |
| 176 | const auto guard = Lock(); | ||
| 164 | const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), | 177 | const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), |
| 165 | [id](const auto& queue) { return queue.GetId() == id; }); | 178 | [id](const auto& queue) { return queue->GetId() == id; }); |
| 166 | 179 | ||
| 167 | ASSERT(itr != buffer_queues.end()); | 180 | if (itr == buffer_queues.end()) { |
| 168 | return *itr; | 181 | return nullptr; |
| 169 | } | 182 | } |
| 170 | |||
| 171 | const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const { | ||
| 172 | const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), | ||
| 173 | [id](const auto& queue) { return queue.GetId() == id; }); | ||
| 174 | 183 | ||
| 175 | ASSERT(itr != buffer_queues.end()); | 184 | return itr->get(); |
| 176 | return *itr; | ||
| 177 | } | 185 | } |
| 178 | 186 | ||
| 179 | VI::Display* NVFlinger::FindDisplay(u64 display_id) { | 187 | 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: | |||
| 75 | [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; | 75 | [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; |
| 76 | 76 | ||
| 77 | /// Obtains a buffer queue identified by the ID. | 77 | /// Obtains a buffer queue identified by the ID. |
| 78 | [[nodiscard]] BufferQueue& FindBufferQueue(u32 id); | 78 | [[nodiscard]] BufferQueue* FindBufferQueue(u32 id); |
| 79 | |||
| 80 | /// Obtains a buffer queue identified by the ID. | ||
| 81 | [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const; | ||
| 82 | 79 | ||
| 83 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when | 80 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when |
| 84 | /// finished. | 81 | /// finished. |
| @@ -86,11 +83,11 @@ public: | |||
| 86 | 83 | ||
| 87 | [[nodiscard]] s64 GetNextTicks() const; | 84 | [[nodiscard]] s64 GetNextTicks() const; |
| 88 | 85 | ||
| 86 | private: | ||
| 89 | [[nodiscard]] std::unique_lock<std::mutex> Lock() const { | 87 | [[nodiscard]] std::unique_lock<std::mutex> Lock() const { |
| 90 | return std::unique_lock{*guard}; | 88 | return std::unique_lock{*guard}; |
| 91 | } | 89 | } |
| 92 | 90 | ||
| 93 | private: | ||
| 94 | /// Finds the display identified by the specified ID. | 91 | /// Finds the display identified by the specified ID. |
| 95 | [[nodiscard]] VI::Display* FindDisplay(u64 display_id); | 92 | [[nodiscard]] VI::Display* FindDisplay(u64 display_id); |
| 96 | 93 | ||
| @@ -110,7 +107,7 @@ private: | |||
| 110 | std::shared_ptr<Nvidia::Module> nvdrv; | 107 | std::shared_ptr<Nvidia::Module> nvdrv; |
| 111 | 108 | ||
| 112 | std::vector<VI::Display> displays; | 109 | std::vector<VI::Display> displays; |
| 113 | std::vector<BufferQueue> buffer_queues; | 110 | std::vector<std::unique_ptr<BufferQueue>> buffer_queues; |
| 114 | 111 | ||
| 115 | /// Id to use for the next layer that is created, this counter is shared among all displays. | 112 | /// Id to use for the next layer that is created, this counter is shared among all displays. |
| 116 | u64 next_layer_id = 1; | 113 | 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 | |||
| 95 | : system{system_}, service_name{service_name_}, max_sessions{max_sessions_}, | 95 | : system{system_}, service_name{service_name_}, max_sessions{max_sessions_}, |
| 96 | handler_invoker{handler_invoker_} {} | 96 | handler_invoker{handler_invoker_} {} |
| 97 | 97 | ||
| 98 | ServiceFrameworkBase::~ServiceFrameworkBase() = default; | 98 | ServiceFrameworkBase::~ServiceFrameworkBase() { |
| 99 | // Wait for other threads to release access before destroying | ||
| 100 | const auto guard = LockService(); | ||
| 101 | } | ||
| 99 | 102 | ||
| 100 | void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { | 103 | void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { |
| 104 | const auto guard = LockService(); | ||
| 105 | |||
| 101 | ASSERT(!port_installed); | 106 | ASSERT(!port_installed); |
| 102 | 107 | ||
| 103 | auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); | 108 | auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); |
| @@ -106,6 +111,8 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) | |||
| 106 | } | 111 | } |
| 107 | 112 | ||
| 108 | void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) { | 113 | void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) { |
| 114 | const auto guard = LockService(); | ||
| 115 | |||
| 109 | ASSERT(!port_installed); | 116 | ASSERT(!port_installed); |
| 110 | 117 | ||
| 111 | auto [server_port, client_port] = | 118 | auto [server_port, client_port] = |
| @@ -115,17 +122,6 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) { | |||
| 115 | port_installed = true; | 122 | port_installed = true; |
| 116 | } | 123 | } |
| 117 | 124 | ||
| 118 | std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) { | ||
| 119 | ASSERT(!port_installed); | ||
| 120 | |||
| 121 | auto [server_port, client_port] = | ||
| 122 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); | ||
| 123 | auto port = MakeResult(std::move(server_port)).Unwrap(); | ||
| 124 | port->SetHleHandler(shared_from_this()); | ||
| 125 | port_installed = true; | ||
| 126 | return client_port; | ||
| 127 | } | ||
| 128 | |||
| 129 | void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { | 125 | void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { |
| 130 | handlers.reserve(handlers.size() + n); | 126 | handlers.reserve(handlers.size() + n); |
| 131 | for (std::size_t i = 0; i < n; ++i) { | 127 | for (std::size_t i = 0; i < n; ++i) { |
| @@ -164,6 +160,8 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { | |||
| 164 | } | 160 | } |
| 165 | 161 | ||
| 166 | ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) { | 162 | ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) { |
| 163 | const auto guard = LockService(); | ||
| 164 | |||
| 167 | switch (context.GetCommandType()) { | 165 | switch (context.GetCommandType()) { |
| 168 | case IPC::CommandType::Close: { | 166 | case IPC::CommandType::Close: { |
| 169 | IPC::ResponseBuilder rb{context, 2}; | 167 | IPC::ResponseBuilder rb{context, 2}; |
| @@ -184,7 +182,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co | |||
| 184 | UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType()); | 182 | UNIMPLEMENTED_MSG("command_type={}", context.GetCommandType()); |
| 185 | } | 183 | } |
| 186 | 184 | ||
| 187 | context.WriteToOutgoingCommandBuffer(context.GetThread()); | 185 | // If emulation was shutdown, we are closing service threads, do not write the response back to |
| 186 | // memory that may be shutting down as well. | ||
| 187 | if (system.IsPoweredOn()) { | ||
| 188 | context.WriteToOutgoingCommandBuffer(context.GetThread()); | ||
| 189 | } | ||
| 188 | 190 | ||
| 189 | return RESULT_SUCCESS; | 191 | return RESULT_SUCCESS; |
| 190 | } | 192 | } |
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 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <mutex> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <boost/container/flat_map.hpp> | 10 | #include <boost/container/flat_map.hpp> |
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/spin_lock.h" | ||
| 11 | #include "core/hle/kernel/hle_ipc.h" | 13 | #include "core/hle/kernel/hle_ipc.h" |
| 12 | #include "core/hle/kernel/object.h" | 14 | #include "core/hle/kernel/object.h" |
| 13 | 15 | ||
| @@ -68,11 +70,9 @@ public: | |||
| 68 | void InstallAsService(SM::ServiceManager& service_manager); | 70 | void InstallAsService(SM::ServiceManager& service_manager); |
| 69 | /// Creates a port pair and registers it on the kernel's global port registry. | 71 | /// Creates a port pair and registers it on the kernel's global port registry. |
| 70 | void InstallAsNamedPort(Kernel::KernelCore& kernel); | 72 | void InstallAsNamedPort(Kernel::KernelCore& kernel); |
| 71 | /// Creates and returns an unregistered port for the service. | 73 | /// Invokes a service request routine. |
| 72 | std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel); | ||
| 73 | |||
| 74 | void InvokeRequest(Kernel::HLERequestContext& ctx); | 74 | void InvokeRequest(Kernel::HLERequestContext& ctx); |
| 75 | 75 | /// Handles a synchronization request for the service. | |
| 76 | ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override; | 76 | ResultCode HandleSyncRequest(Kernel::HLERequestContext& context) override; |
| 77 | 77 | ||
| 78 | protected: | 78 | protected: |
| @@ -80,6 +80,11 @@ protected: | |||
| 80 | template <typename Self> | 80 | template <typename Self> |
| 81 | using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); | 81 | using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); |
| 82 | 82 | ||
| 83 | /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. | ||
| 84 | [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() { | ||
| 85 | return std::scoped_lock{lock_service}; | ||
| 86 | } | ||
| 87 | |||
| 83 | /// System context that the service operates under. | 88 | /// System context that the service operates under. |
| 84 | Core::System& system; | 89 | Core::System& system; |
| 85 | 90 | ||
| @@ -115,6 +120,9 @@ private: | |||
| 115 | /// Function used to safely up-cast pointers to the derived class before invoking a handler. | 120 | /// Function used to safely up-cast pointers to the derived class before invoking a handler. |
| 116 | InvokerFn* handler_invoker; | 121 | InvokerFn* handler_invoker; |
| 117 | boost::container::flat_map<u32, FunctionInfoBase> handlers; | 122 | boost::container::flat_map<u32, FunctionInfoBase> handlers; |
| 123 | |||
| 124 | /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. | ||
| 125 | Common::SpinLock lock_service; | ||
| 118 | }; | 126 | }; |
| 119 | 127 | ||
| 120 | /** | 128 | /** |
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 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <string_view> | ||
| 11 | #include <thread> | ||
| 12 | #include <variant> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include <fmt/format.h> | ||
| 16 | |||
| 17 | #include "common/assert.h" | ||
| 18 | #include "common/microprofile.h" | ||
| 19 | #include "common/thread.h" | ||
| 20 | #include "core/core.h" | ||
| 21 | #include "core/hle/kernel/hle_ipc.h" | ||
| 22 | #include "core/hle/kernel/kernel.h" | ||
| 23 | #include "core/hle/kernel/thread.h" | ||
| 24 | #include "core/hle/kernel/writable_event.h" | ||
| 25 | |||
| 26 | namespace Service::Sockets { | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Worker abstraction to execute blocking calls on host without blocking the guest thread | ||
| 30 | * | ||
| 31 | * @tparam Service Service where the work is executed | ||
| 32 | * @tparam Types Types of work to execute | ||
| 33 | */ | ||
| 34 | template <class Service, class... Types> | ||
| 35 | class BlockingWorker { | ||
| 36 | using This = BlockingWorker<Service, Types...>; | ||
| 37 | using WorkVariant = std::variant<std::monostate, Types...>; | ||
| 38 | |||
| 39 | public: | ||
| 40 | /// Create a new worker | ||
| 41 | static std::unique_ptr<This> Create(Core::System& system, Service* service, | ||
| 42 | std::string_view name) { | ||
| 43 | return std::unique_ptr<This>(new This(system, service, name)); | ||
| 44 | } | ||
| 45 | |||
| 46 | ~BlockingWorker() { | ||
| 47 | while (!is_available.load(std::memory_order_relaxed)) { | ||
| 48 | // Busy wait until work is finished | ||
| 49 | std::this_thread::yield(); | ||
| 50 | } | ||
| 51 | // Monostate means to exit the thread | ||
| 52 | work = std::monostate{}; | ||
| 53 | work_event.Set(); | ||
| 54 | thread.join(); | ||
| 55 | } | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Try to capture the worker to send work after a success | ||
| 59 | * @returns True when the worker has been successfully captured | ||
| 60 | */ | ||
| 61 | bool TryCapture() { | ||
| 62 | bool expected = true; | ||
| 63 | return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed, | ||
| 64 | std::memory_order_relaxed); | ||
| 65 | } | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Send work to this worker abstraction | ||
| 69 | * @see TryCapture must be called before attempting to call this function | ||
| 70 | */ | ||
| 71 | template <class Work> | ||
| 72 | void SendWork(Work new_work) { | ||
| 73 | ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured"); | ||
| 74 | work = std::move(new_work); | ||
| 75 | work_event.Set(); | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Generate a callback for @see SleepClientThread | ||
| 79 | template <class Work> | ||
| 80 | auto Callback() { | ||
| 81 | return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx, | ||
| 82 | Kernel::ThreadWakeupReason reason) { | ||
| 83 | ASSERT(reason == Kernel::ThreadWakeupReason::Signal); | ||
| 84 | std::get<Work>(work).Response(ctx); | ||
| 85 | is_available.store(true); | ||
| 86 | }; | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Get kernel event that will be signalled by the worker when the host operation finishes | ||
| 90 | std::shared_ptr<Kernel::WritableEvent> KernelEvent() const { | ||
| 91 | return kernel_event; | ||
| 92 | } | ||
| 93 | |||
| 94 | private: | ||
| 95 | explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) { | ||
| 96 | auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name)); | ||
| 97 | kernel_event = std::move(pair.writable); | ||
| 98 | thread = std::thread([this, &system, service, name] { Run(system, service, name); }); | ||
| 99 | } | ||
| 100 | |||
| 101 | void Run(Core::System& system, Service* service, std::string_view name) { | ||
| 102 | system.RegisterHostThread(); | ||
| 103 | |||
| 104 | const std::string thread_name = fmt::format("yuzu:{}", name); | ||
| 105 | MicroProfileOnThreadCreate(thread_name.c_str()); | ||
| 106 | Common::SetCurrentThreadName(thread_name.c_str()); | ||
| 107 | |||
| 108 | bool keep_running = true; | ||
| 109 | while (keep_running) { | ||
| 110 | work_event.Wait(); | ||
| 111 | |||
| 112 | const auto visit_fn = [service, &keep_running]<typename T>(T&& w) { | ||
| 113 | if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) { | ||
| 114 | keep_running = false; | ||
| 115 | } else { | ||
| 116 | w.Execute(service); | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | std::visit(visit_fn, work); | ||
| 120 | |||
| 121 | kernel_event->Signal(); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | std::thread thread; | ||
| 126 | WorkVariant work; | ||
| 127 | Common::Event work_event; | ||
| 128 | std::shared_ptr<Kernel::WritableEvent> kernel_event; | ||
| 129 | std::atomic_bool is_available{true}; | ||
| 130 | }; | ||
| 131 | |||
| 132 | template <class Service, class... Types> | ||
| 133 | class BlockingWorkerPool { | ||
| 134 | using Worker = BlockingWorker<Service, Types...>; | ||
| 135 | |||
| 136 | public: | ||
| 137 | explicit BlockingWorkerPool(Core::System& system_, Service* service_) | ||
| 138 | : system{system_}, service{service_} {} | ||
| 139 | |||
| 140 | /// Returns a captured worker thread, creating new ones if necessary | ||
| 141 | Worker* CaptureWorker() { | ||
| 142 | for (auto& worker : workers) { | ||
| 143 | if (worker->TryCapture()) { | ||
| 144 | return worker.get(); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size())); | ||
| 148 | [[maybe_unused]] const bool success = new_worker->TryCapture(); | ||
| 149 | ASSERT(success); | ||
| 150 | |||
| 151 | return workers.emplace_back(std::move(new_worker)).get(); | ||
| 152 | } | ||
| 153 | |||
| 154 | private: | ||
| 155 | Core::System& system; | ||
| 156 | Service* const service; | ||
| 157 | |||
| 158 | std::vector<std::unique_ptr<Worker>> workers; | ||
| 159 | }; | ||
| 160 | |||
| 161 | } // 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) { | |||
| 178 | 178 | ||
| 179 | LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout); | 179 | LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout); |
| 180 | 180 | ||
| 181 | ExecuteWork(ctx, "BSD:Poll", timeout != 0, | 181 | ExecuteWork(ctx, PollWork{ |
| 182 | PollWork{ | 182 | .nfds = nfds, |
| 183 | .nfds = nfds, | 183 | .timeout = timeout, |
| 184 | .timeout = timeout, | 184 | .read_buffer = ctx.ReadBuffer(), |
| 185 | .read_buffer = ctx.ReadBuffer(), | 185 | .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), |
| 186 | .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), | 186 | }); |
| 187 | }); | ||
| 188 | } | 187 | } |
| 189 | 188 | ||
| 190 | void BSD::Accept(Kernel::HLERequestContext& ctx) { | 189 | void BSD::Accept(Kernel::HLERequestContext& ctx) { |
| @@ -193,11 +192,10 @@ void BSD::Accept(Kernel::HLERequestContext& ctx) { | |||
| 193 | 192 | ||
| 194 | LOG_DEBUG(Service, "called. fd={}", fd); | 193 | LOG_DEBUG(Service, "called. fd={}", fd); |
| 195 | 194 | ||
| 196 | ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd), | 195 | ExecuteWork(ctx, AcceptWork{ |
| 197 | AcceptWork{ | 196 | .fd = fd, |
| 198 | .fd = fd, | 197 | .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), |
| 199 | .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), | 198 | }); |
| 200 | }); | ||
| 201 | } | 199 | } |
| 202 | 200 | ||
| 203 | void BSD::Bind(Kernel::HLERequestContext& ctx) { | 201 | void BSD::Bind(Kernel::HLERequestContext& ctx) { |
| @@ -215,11 +213,10 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) { | |||
| 215 | 213 | ||
| 216 | LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); | 214 | LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); |
| 217 | 215 | ||
| 218 | ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd), | 216 | ExecuteWork(ctx, ConnectWork{ |
| 219 | ConnectWork{ | 217 | .fd = fd, |
| 220 | .fd = fd, | 218 | .addr = ctx.ReadBuffer(), |
| 221 | .addr = ctx.ReadBuffer(), | 219 | }); |
| 222 | }); | ||
| 223 | } | 220 | } |
| 224 | 221 | ||
| 225 | void BSD::GetPeerName(Kernel::HLERequestContext& ctx) { | 222 | void BSD::GetPeerName(Kernel::HLERequestContext& ctx) { |
| @@ -327,12 +324,11 @@ void BSD::Recv(Kernel::HLERequestContext& ctx) { | |||
| 327 | 324 | ||
| 328 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize()); | 325 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize()); |
| 329 | 326 | ||
| 330 | ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd), | 327 | ExecuteWork(ctx, RecvWork{ |
| 331 | RecvWork{ | 328 | .fd = fd, |
| 332 | .fd = fd, | 329 | .flags = flags, |
| 333 | .flags = flags, | 330 | .message = std::vector<u8>(ctx.GetWriteBufferSize()), |
| 334 | .message = std::vector<u8>(ctx.GetWriteBufferSize()), | 331 | }); |
| 335 | }); | ||
| 336 | } | 332 | } |
| 337 | 333 | ||
| 338 | void BSD::RecvFrom(Kernel::HLERequestContext& ctx) { | 334 | void BSD::RecvFrom(Kernel::HLERequestContext& ctx) { |
| @@ -344,13 +340,12 @@ void BSD::RecvFrom(Kernel::HLERequestContext& ctx) { | |||
| 344 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags, | 340 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags, |
| 345 | ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1)); | 341 | ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1)); |
| 346 | 342 | ||
| 347 | ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd), | 343 | ExecuteWork(ctx, RecvFromWork{ |
| 348 | RecvFromWork{ | 344 | .fd = fd, |
| 349 | .fd = fd, | 345 | .flags = flags, |
| 350 | .flags = flags, | 346 | .message = std::vector<u8>(ctx.GetWriteBufferSize(0)), |
| 351 | .message = std::vector<u8>(ctx.GetWriteBufferSize(0)), | 347 | .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)), |
| 352 | .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)), | 348 | }); |
| 353 | }); | ||
| 354 | } | 349 | } |
| 355 | 350 | ||
| 356 | void BSD::Send(Kernel::HLERequestContext& ctx) { | 351 | void BSD::Send(Kernel::HLERequestContext& ctx) { |
| @@ -361,12 +356,11 @@ void BSD::Send(Kernel::HLERequestContext& ctx) { | |||
| 361 | 356 | ||
| 362 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize()); | 357 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize()); |
| 363 | 358 | ||
| 364 | ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd), | 359 | ExecuteWork(ctx, SendWork{ |
| 365 | SendWork{ | 360 | .fd = fd, |
| 366 | .fd = fd, | 361 | .flags = flags, |
| 367 | .flags = flags, | 362 | .message = ctx.ReadBuffer(), |
| 368 | .message = ctx.ReadBuffer(), | 363 | }); |
| 369 | }); | ||
| 370 | } | 364 | } |
| 371 | 365 | ||
| 372 | void BSD::SendTo(Kernel::HLERequestContext& ctx) { | 366 | void BSD::SendTo(Kernel::HLERequestContext& ctx) { |
| @@ -377,13 +371,12 @@ void BSD::SendTo(Kernel::HLERequestContext& ctx) { | |||
| 377 | LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags, | 371 | LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags, |
| 378 | ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1)); | 372 | ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1)); |
| 379 | 373 | ||
| 380 | ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd), | 374 | ExecuteWork(ctx, SendToWork{ |
| 381 | SendToWork{ | 375 | .fd = fd, |
| 382 | .fd = fd, | 376 | .flags = flags, |
| 383 | .flags = flags, | 377 | .message = ctx.ReadBuffer(0), |
| 384 | .message = ctx.ReadBuffer(0), | 378 | .addr = ctx.ReadBuffer(1), |
| 385 | .addr = ctx.ReadBuffer(1), | 379 | }); |
| 386 | }); | ||
| 387 | } | 380 | } |
| 388 | 381 | ||
| 389 | void BSD::Write(Kernel::HLERequestContext& ctx) { | 382 | void BSD::Write(Kernel::HLERequestContext& ctx) { |
| @@ -392,12 +385,11 @@ void BSD::Write(Kernel::HLERequestContext& ctx) { | |||
| 392 | 385 | ||
| 393 | LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize()); | 386 | LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize()); |
| 394 | 387 | ||
| 395 | ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd), | 388 | ExecuteWork(ctx, SendWork{ |
| 396 | SendWork{ | 389 | .fd = fd, |
| 397 | .fd = fd, | 390 | .flags = 0, |
| 398 | .flags = 0, | 391 | .message = ctx.ReadBuffer(), |
| 399 | .message = ctx.ReadBuffer(), | 392 | }); |
| 400 | }); | ||
| 401 | } | 393 | } |
| 402 | 394 | ||
| 403 | void BSD::Close(Kernel::HLERequestContext& ctx) { | 395 | void BSD::Close(Kernel::HLERequestContext& ctx) { |
| @@ -410,24 +402,9 @@ void BSD::Close(Kernel::HLERequestContext& ctx) { | |||
| 410 | } | 402 | } |
| 411 | 403 | ||
| 412 | template <typename Work> | 404 | template <typename Work> |
| 413 | void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, | 405 | void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) { |
| 414 | bool is_blocking, Work work) { | 406 | work.Execute(this); |
| 415 | if (!is_blocking) { | ||
| 416 | work.Execute(this); | ||
| 417 | work.Response(ctx); | ||
| 418 | return; | ||
| 419 | } | ||
| 420 | |||
| 421 | // Signal a dummy response to make IPC validation happy | ||
| 422 | // This will be overwritten by the SleepClientThread callback | ||
| 423 | work.Response(ctx); | 407 | work.Response(ctx); |
| 424 | |||
| 425 | auto worker = worker_pool.CaptureWorker(); | ||
| 426 | |||
| 427 | ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(), | ||
| 428 | worker->Callback<Work>(), worker->KernelEvent()); | ||
| 429 | |||
| 430 | worker->SendWork(std::move(work)); | ||
| 431 | } | 408 | } |
| 432 | 409 | ||
| 433 | std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) { | 410 | std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) { |
| @@ -807,18 +784,6 @@ bool BSD::IsFileDescriptorValid(s32 fd) const noexcept { | |||
| 807 | return true; | 784 | return true; |
| 808 | } | 785 | } |
| 809 | 786 | ||
| 810 | bool BSD::IsBlockingSocket(s32 fd) const noexcept { | ||
| 811 | // Inform invalid sockets as non-blocking | ||
| 812 | // This way we avoid using a worker thread as it will fail without blocking host | ||
| 813 | if (fd > static_cast<s32>(MAX_FD) || fd < 0) { | ||
| 814 | return false; | ||
| 815 | } | ||
| 816 | if (!file_descriptors[fd]) { | ||
| 817 | return false; | ||
| 818 | } | ||
| 819 | return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0; | ||
| 820 | } | ||
| 821 | |||
| 822 | void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept { | 787 | void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept { |
| 823 | IPC::ResponseBuilder rb{ctx, 4}; | 788 | IPC::ResponseBuilder rb{ctx, 4}; |
| 824 | 789 | ||
| @@ -827,8 +792,7 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co | |||
| 827 | rb.PushEnum(bsd_errno); | 792 | rb.PushEnum(bsd_errno); |
| 828 | } | 793 | } |
| 829 | 794 | ||
| 830 | BSD::BSD(Core::System& system_, const char* name) | 795 | BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { |
| 831 | : ServiceFramework{system_, name}, worker_pool{system_, this} { | ||
| 832 | // clang-format off | 796 | // clang-format off |
| 833 | static const FunctionInfo functions[] = { | 797 | static const FunctionInfo functions[] = { |
| 834 | {0, &BSD::RegisterClient, "RegisterClient"}, | 798 | {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 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/kernel/hle_ipc.h" | 12 | #include "core/hle/kernel/hle_ipc.h" |
| 13 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 14 | #include "core/hle/service/sockets/blocking_worker.h" | ||
| 15 | #include "core/hle/service/sockets/sockets.h" | 14 | #include "core/hle/service/sockets/sockets.h" |
| 16 | 15 | ||
| 17 | namespace Core { | 16 | namespace Core { |
| @@ -138,8 +137,7 @@ private: | |||
| 138 | void Close(Kernel::HLERequestContext& ctx); | 137 | void Close(Kernel::HLERequestContext& ctx); |
| 139 | 138 | ||
| 140 | template <typename Work> | 139 | template <typename Work> |
| 141 | void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, | 140 | void ExecuteWork(Kernel::HLERequestContext& ctx, Work work); |
| 142 | bool is_blocking, Work work); | ||
| 143 | 141 | ||
| 144 | std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol); | 142 | std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol); |
| 145 | std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, | 143 | std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, |
| @@ -163,15 +161,10 @@ private: | |||
| 163 | 161 | ||
| 164 | s32 FindFreeFileDescriptorHandle() noexcept; | 162 | s32 FindFreeFileDescriptorHandle() noexcept; |
| 165 | bool IsFileDescriptorValid(s32 fd) const noexcept; | 163 | bool IsFileDescriptorValid(s32 fd) const noexcept; |
| 166 | bool IsBlockingSocket(s32 fd) const noexcept; | ||
| 167 | 164 | ||
| 168 | void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; | 165 | void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; |
| 169 | 166 | ||
| 170 | std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; | 167 | std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; |
| 171 | |||
| 172 | BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork, | ||
| 173 | SendToWork> | ||
| 174 | worker_pool; | ||
| 175 | }; | 168 | }; |
| 176 | 169 | ||
| 177 | class BSDCFG final : public ServiceFramework<BSDCFG> { | 170 | 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: | |||
| 536 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, | 536 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, |
| 537 | transaction, flags); | 537 | transaction, flags); |
| 538 | 538 | ||
| 539 | const auto guard = nv_flinger.Lock(); | 539 | auto& buffer_queue = *nv_flinger.FindBufferQueue(id); |
| 540 | auto& buffer_queue = nv_flinger.FindBufferQueue(id); | ||
| 541 | 540 | ||
| 542 | switch (transaction) { | 541 | switch (transaction) { |
| 543 | case TransactionId::Connect: { | 542 | case TransactionId::Connect: { |
| @@ -547,6 +546,9 @@ private: | |||
| 547 | Settings::values.resolution_factor.GetValue()), | 546 | Settings::values.resolution_factor.GetValue()), |
| 548 | static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * | 547 | static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * |
| 549 | Settings::values.resolution_factor.GetValue())}; | 548 | Settings::values.resolution_factor.GetValue())}; |
| 549 | |||
| 550 | buffer_queue.Connect(); | ||
| 551 | |||
| 550 | ctx.WriteBuffer(response.Serialize()); | 552 | ctx.WriteBuffer(response.Serialize()); |
| 551 | break; | 553 | break; |
| 552 | } | 554 | } |
| @@ -563,40 +565,25 @@ private: | |||
| 563 | IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; | 565 | IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; |
| 564 | const u32 width{request.data.width}; | 566 | const u32 width{request.data.width}; |
| 565 | const u32 height{request.data.height}; | 567 | const u32 height{request.data.height}; |
| 566 | auto result = buffer_queue.DequeueBuffer(width, height); | 568 | |
| 567 | 569 | do { | |
| 568 | if (result) { | 570 | if (auto result = buffer_queue.DequeueBuffer(width, height); result) { |
| 569 | // Buffer is available | 571 | // Buffer is available |
| 570 | IGBPDequeueBufferResponseParcel response{result->first, *result->second}; | 572 | IGBPDequeueBufferResponseParcel response{result->first, *result->second}; |
| 571 | ctx.WriteBuffer(response.Serialize()); | 573 | ctx.WriteBuffer(response.Serialize()); |
| 572 | } else { | 574 | break; |
| 573 | // Wait the current thread until a buffer becomes available | 575 | } |
| 574 | ctx.SleepClientThread( | 576 | } while (buffer_queue.IsConnected()); |
| 575 | "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, | 577 | |
| 576 | [=, this](std::shared_ptr<Kernel::Thread> thread, | ||
| 577 | Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { | ||
| 578 | // Repeat TransactParcel DequeueBuffer when a buffer is available | ||
| 579 | const auto guard = nv_flinger.Lock(); | ||
| 580 | auto& buffer_queue = nv_flinger.FindBufferQueue(id); | ||
| 581 | auto result = buffer_queue.DequeueBuffer(width, height); | ||
| 582 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); | ||
| 583 | |||
| 584 | IGBPDequeueBufferResponseParcel response{result->first, *result->second}; | ||
| 585 | ctx.WriteBuffer(response.Serialize()); | ||
| 586 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 587 | rb.Push(RESULT_SUCCESS); | ||
| 588 | }, | ||
| 589 | buffer_queue.GetWritableBufferWaitEvent()); | ||
| 590 | } | ||
| 591 | break; | 578 | break; |
| 592 | } | 579 | } |
| 593 | case TransactionId::RequestBuffer: { | 580 | case TransactionId::RequestBuffer: { |
| 594 | IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; | 581 | IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; |
| 595 | 582 | ||
| 596 | auto& buffer = buffer_queue.RequestBuffer(request.slot); | 583 | auto& buffer = buffer_queue.RequestBuffer(request.slot); |
| 597 | |||
| 598 | IGBPRequestBufferResponseParcel response{buffer}; | 584 | IGBPRequestBufferResponseParcel response{buffer}; |
| 599 | ctx.WriteBuffer(response.Serialize()); | 585 | ctx.WriteBuffer(response.Serialize()); |
| 586 | |||
| 600 | break; | 587 | break; |
| 601 | } | 588 | } |
| 602 | case TransactionId::QueueBuffer: { | 589 | case TransactionId::QueueBuffer: { |
| @@ -682,7 +669,7 @@ private: | |||
| 682 | 669 | ||
| 683 | LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); | 670 | LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); |
| 684 | 671 | ||
| 685 | const auto& buffer_queue = nv_flinger.FindBufferQueue(id); | 672 | const auto& buffer_queue = *nv_flinger.FindBufferQueue(id); |
| 686 | 673 | ||
| 687 | // TODO(Subv): Find out what this actually is. | 674 | // TODO(Subv): Find out what this actually is. |
| 688 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 675 | 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 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <mutex> | ||
| 8 | #include <optional> | 7 | #include <optional> |
| 9 | #include <utility> | 8 | #include <utility> |
| 10 | 9 | ||
| @@ -45,44 +44,16 @@ struct Memory::Impl { | |||
| 45 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); | 44 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); |
| 46 | } | 45 | } |
| 47 | 46 | ||
| 48 | void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 49 | Common::MemoryHookPointer mmio_handler) { | ||
| 50 | UNIMPLEMENTED(); | ||
| 51 | } | ||
| 52 | |||
| 53 | void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { | 47 | void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { |
| 54 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); | 48 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); |
| 55 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); | 49 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); |
| 56 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped); | 50 | MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped); |
| 57 | } | 51 | } |
| 58 | 52 | ||
| 59 | void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 60 | Common::MemoryHookPointer hook) { | ||
| 61 | UNIMPLEMENTED(); | ||
| 62 | } | ||
| 63 | |||
| 64 | void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 65 | Common::MemoryHookPointer hook) { | ||
| 66 | UNIMPLEMENTED(); | ||
| 67 | } | ||
| 68 | |||
| 69 | bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { | 53 | bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { |
| 70 | const auto& page_table = process.PageTable().PageTableImpl(); | 54 | const auto& page_table = process.PageTable().PageTableImpl(); |
| 71 | 55 | const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType(); | |
| 72 | const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; | 56 | return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory; |
| 73 | if (page_pointer != nullptr) { | ||
| 74 | return true; | ||
| 75 | } | ||
| 76 | |||
| 77 | if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) { | ||
| 78 | return true; | ||
| 79 | } | ||
| 80 | |||
| 81 | if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) { | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | |||
| 85 | return false; | ||
| 86 | } | 57 | } |
| 87 | 58 | ||
| 88 | bool IsValidVirtualAddress(VAddr vaddr) const { | 59 | bool IsValidVirtualAddress(VAddr vaddr) const { |
| @@ -100,17 +71,15 @@ struct Memory::Impl { | |||
| 100 | } | 71 | } |
| 101 | 72 | ||
| 102 | u8* GetPointer(const VAddr vaddr) const { | 73 | u8* GetPointer(const VAddr vaddr) const { |
| 103 | u8* const page_pointer{current_page_table->pointers[vaddr >> PAGE_BITS]}; | 74 | const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); |
| 104 | if (page_pointer) { | 75 | if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { |
| 105 | return page_pointer + vaddr; | 76 | return pointer + vaddr; |
| 106 | } | 77 | } |
| 107 | 78 | const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer); | |
| 108 | if (current_page_table->attributes[vaddr >> PAGE_BITS] == | 79 | if (type == Common::PageType::RasterizerCachedMemory) { |
| 109 | Common::PageType::RasterizerCachedMemory) { | ||
| 110 | return GetPointerFromRasterizerCachedMemory(vaddr); | 80 | return GetPointerFromRasterizerCachedMemory(vaddr); |
| 111 | } | 81 | } |
| 112 | 82 | return nullptr; | |
| 113 | return {}; | ||
| 114 | } | 83 | } |
| 115 | 84 | ||
| 116 | u8 Read8(const VAddr addr) { | 85 | u8 Read8(const VAddr addr) { |
| @@ -222,7 +191,8 @@ struct Memory::Impl { | |||
| 222 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | 191 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 223 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 192 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 224 | 193 | ||
| 225 | switch (page_table.attributes[page_index]) { | 194 | const auto [pointer, type] = page_table.pointers[page_index].PointerType(); |
| 195 | switch (type) { | ||
| 226 | case Common::PageType::Unmapped: { | 196 | case Common::PageType::Unmapped: { |
| 227 | LOG_ERROR(HW_Memory, | 197 | LOG_ERROR(HW_Memory, |
| 228 | "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | 198 | "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", |
| @@ -231,10 +201,8 @@ struct Memory::Impl { | |||
| 231 | break; | 201 | break; |
| 232 | } | 202 | } |
| 233 | case Common::PageType::Memory: { | 203 | case Common::PageType::Memory: { |
| 234 | DEBUG_ASSERT(page_table.pointers[page_index]); | 204 | DEBUG_ASSERT(pointer); |
| 235 | 205 | const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS); | |
| 236 | const u8* const src_ptr = | ||
| 237 | page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); | ||
| 238 | std::memcpy(dest_buffer, src_ptr, copy_amount); | 206 | std::memcpy(dest_buffer, src_ptr, copy_amount); |
| 239 | break; | 207 | break; |
| 240 | } | 208 | } |
| @@ -268,7 +236,8 @@ struct Memory::Impl { | |||
| 268 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | 236 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 269 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 237 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 270 | 238 | ||
| 271 | switch (page_table.attributes[page_index]) { | 239 | const auto [pointer, type] = page_table.pointers[page_index].PointerType(); |
| 240 | switch (type) { | ||
| 272 | case Common::PageType::Unmapped: { | 241 | case Common::PageType::Unmapped: { |
| 273 | LOG_ERROR(HW_Memory, | 242 | LOG_ERROR(HW_Memory, |
| 274 | "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | 243 | "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", |
| @@ -277,10 +246,8 @@ struct Memory::Impl { | |||
| 277 | break; | 246 | break; |
| 278 | } | 247 | } |
| 279 | case Common::PageType::Memory: { | 248 | case Common::PageType::Memory: { |
| 280 | DEBUG_ASSERT(page_table.pointers[page_index]); | 249 | DEBUG_ASSERT(pointer); |
| 281 | 250 | const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS); | |
| 282 | const u8* const src_ptr = | ||
| 283 | page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); | ||
| 284 | std::memcpy(dest_buffer, src_ptr, copy_amount); | 251 | std::memcpy(dest_buffer, src_ptr, copy_amount); |
| 285 | break; | 252 | break; |
| 286 | } | 253 | } |
| @@ -320,7 +287,8 @@ struct Memory::Impl { | |||
| 320 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | 287 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 321 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 288 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 322 | 289 | ||
| 323 | switch (page_table.attributes[page_index]) { | 290 | const auto [pointer, type] = page_table.pointers[page_index].PointerType(); |
| 291 | switch (type) { | ||
| 324 | case Common::PageType::Unmapped: { | 292 | case Common::PageType::Unmapped: { |
| 325 | LOG_ERROR(HW_Memory, | 293 | LOG_ERROR(HW_Memory, |
| 326 | "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | 294 | "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", |
| @@ -328,10 +296,8 @@ struct Memory::Impl { | |||
| 328 | break; | 296 | break; |
| 329 | } | 297 | } |
| 330 | case Common::PageType::Memory: { | 298 | case Common::PageType::Memory: { |
| 331 | DEBUG_ASSERT(page_table.pointers[page_index]); | 299 | DEBUG_ASSERT(pointer); |
| 332 | 300 | u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS); | |
| 333 | u8* const dest_ptr = | ||
| 334 | page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); | ||
| 335 | std::memcpy(dest_ptr, src_buffer, copy_amount); | 301 | std::memcpy(dest_ptr, src_buffer, copy_amount); |
| 336 | break; | 302 | break; |
| 337 | } | 303 | } |
| @@ -364,7 +330,8 @@ struct Memory::Impl { | |||
| 364 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | 330 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 365 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 331 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 366 | 332 | ||
| 367 | switch (page_table.attributes[page_index]) { | 333 | const auto [pointer, type] = page_table.pointers[page_index].PointerType(); |
| 334 | switch (type) { | ||
| 368 | case Common::PageType::Unmapped: { | 335 | case Common::PageType::Unmapped: { |
| 369 | LOG_ERROR(HW_Memory, | 336 | LOG_ERROR(HW_Memory, |
| 370 | "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | 337 | "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", |
| @@ -372,10 +339,8 @@ struct Memory::Impl { | |||
| 372 | break; | 339 | break; |
| 373 | } | 340 | } |
| 374 | case Common::PageType::Memory: { | 341 | case Common::PageType::Memory: { |
| 375 | DEBUG_ASSERT(page_table.pointers[page_index]); | 342 | DEBUG_ASSERT(pointer); |
| 376 | 343 | u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS); | |
| 377 | u8* const dest_ptr = | ||
| 378 | page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); | ||
| 379 | std::memcpy(dest_ptr, src_buffer, copy_amount); | 344 | std::memcpy(dest_ptr, src_buffer, copy_amount); |
| 380 | break; | 345 | break; |
| 381 | } | 346 | } |
| @@ -414,7 +379,8 @@ struct Memory::Impl { | |||
| 414 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | 379 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 415 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 380 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 416 | 381 | ||
| 417 | switch (page_table.attributes[page_index]) { | 382 | const auto [pointer, type] = page_table.pointers[page_index].PointerType(); |
| 383 | switch (type) { | ||
| 418 | case Common::PageType::Unmapped: { | 384 | case Common::PageType::Unmapped: { |
| 419 | LOG_ERROR(HW_Memory, | 385 | LOG_ERROR(HW_Memory, |
| 420 | "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | 386 | "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", |
| @@ -422,10 +388,8 @@ struct Memory::Impl { | |||
| 422 | break; | 388 | break; |
| 423 | } | 389 | } |
| 424 | case Common::PageType::Memory: { | 390 | case Common::PageType::Memory: { |
| 425 | DEBUG_ASSERT(page_table.pointers[page_index]); | 391 | DEBUG_ASSERT(pointer); |
| 426 | 392 | u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS); | |
| 427 | u8* dest_ptr = | ||
| 428 | page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); | ||
| 429 | std::memset(dest_ptr, 0, copy_amount); | 393 | std::memset(dest_ptr, 0, copy_amount); |
| 430 | break; | 394 | break; |
| 431 | } | 395 | } |
| @@ -461,7 +425,8 @@ struct Memory::Impl { | |||
| 461 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | 425 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 462 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 426 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 463 | 427 | ||
| 464 | switch (page_table.attributes[page_index]) { | 428 | const auto [pointer, type] = page_table.pointers[page_index].PointerType(); |
| 429 | switch (type) { | ||
| 465 | case Common::PageType::Unmapped: { | 430 | case Common::PageType::Unmapped: { |
| 466 | LOG_ERROR(HW_Memory, | 431 | LOG_ERROR(HW_Memory, |
| 467 | "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | 432 | "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", |
| @@ -470,9 +435,8 @@ struct Memory::Impl { | |||
| 470 | break; | 435 | break; |
| 471 | } | 436 | } |
| 472 | case Common::PageType::Memory: { | 437 | case Common::PageType::Memory: { |
| 473 | DEBUG_ASSERT(page_table.pointers[page_index]); | 438 | DEBUG_ASSERT(pointer); |
| 474 | const u8* src_ptr = | 439 | const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS); |
| 475 | page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); | ||
| 476 | WriteBlock(process, dest_addr, src_ptr, copy_amount); | 440 | WriteBlock(process, dest_addr, src_ptr, copy_amount); |
| 477 | break; | 441 | break; |
| 478 | } | 442 | } |
| @@ -498,34 +462,19 @@ struct Memory::Impl { | |||
| 498 | return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size); | 462 | return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size); |
| 499 | } | 463 | } |
| 500 | 464 | ||
| 501 | struct PageEntry { | ||
| 502 | u8* const pointer; | ||
| 503 | const Common::PageType attribute; | ||
| 504 | }; | ||
| 505 | |||
| 506 | PageEntry SafePageEntry(std::size_t base) const { | ||
| 507 | std::lock_guard lock{rasterizer_cache_guard}; | ||
| 508 | return { | ||
| 509 | .pointer = current_page_table->pointers[base], | ||
| 510 | .attribute = current_page_table->attributes[base], | ||
| 511 | }; | ||
| 512 | } | ||
| 513 | |||
| 514 | void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { | 465 | void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { |
| 515 | std::lock_guard lock{rasterizer_cache_guard}; | ||
| 516 | if (vaddr == 0) { | 466 | if (vaddr == 0) { |
| 517 | return; | 467 | return; |
| 518 | } | 468 | } |
| 519 | |||
| 520 | // Iterate over a contiguous CPU address space, which corresponds to the specified GPU | 469 | // Iterate over a contiguous CPU address space, which corresponds to the specified GPU |
| 521 | // address space, marking the region as un/cached. The region is marked un/cached at a | 470 | // address space, marking the region as un/cached. The region is marked un/cached at a |
| 522 | // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size | 471 | // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size |
| 523 | // is different). This assumes the specified GPU address region is contiguous as well. | 472 | // is different). This assumes the specified GPU address region is contiguous as well. |
| 524 | 473 | ||
| 525 | u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; | 474 | const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; |
| 526 | for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { | 475 | for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { |
| 527 | Common::PageType& page_type{current_page_table->attributes[vaddr >> PAGE_BITS]}; | 476 | const Common::PageType page_type{ |
| 528 | 477 | current_page_table->pointers[vaddr >> PAGE_BITS].Type()}; | |
| 529 | if (cached) { | 478 | if (cached) { |
| 530 | // Switch page type to cached if now cached | 479 | // Switch page type to cached if now cached |
| 531 | switch (page_type) { | 480 | switch (page_type) { |
| @@ -534,8 +483,8 @@ struct Memory::Impl { | |||
| 534 | // space, for example, a system module need not have a VRAM mapping. | 483 | // space, for example, a system module need not have a VRAM mapping. |
| 535 | break; | 484 | break; |
| 536 | case Common::PageType::Memory: | 485 | case Common::PageType::Memory: |
| 537 | page_type = Common::PageType::RasterizerCachedMemory; | 486 | current_page_table->pointers[vaddr >> PAGE_BITS].Store( |
| 538 | current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; | 487 | nullptr, Common::PageType::RasterizerCachedMemory); |
| 539 | break; | 488 | break; |
| 540 | case Common::PageType::RasterizerCachedMemory: | 489 | case Common::PageType::RasterizerCachedMemory: |
| 541 | // There can be more than one GPU region mapped per CPU region, so it's common | 490 | // There can be more than one GPU region mapped per CPU region, so it's common |
| @@ -556,16 +505,16 @@ struct Memory::Impl { | |||
| 556 | // that this area is already unmarked as cached. | 505 | // that this area is already unmarked as cached. |
| 557 | break; | 506 | break; |
| 558 | case Common::PageType::RasterizerCachedMemory: { | 507 | case Common::PageType::RasterizerCachedMemory: { |
| 559 | u8* pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; | 508 | u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; |
| 560 | if (pointer == nullptr) { | 509 | if (pointer == nullptr) { |
| 561 | // It's possible that this function has been called while updating the | 510 | // It's possible that this function has been called while updating the |
| 562 | // pagetable after unmapping a VMA. In that case the underlying VMA will no | 511 | // pagetable after unmapping a VMA. In that case the underlying VMA will no |
| 563 | // longer exist, and we should just leave the pagetable entry blank. | 512 | // longer exist, and we should just leave the pagetable entry blank. |
| 564 | page_type = Common::PageType::Unmapped; | 513 | current_page_table->pointers[vaddr >> PAGE_BITS].Store( |
| 514 | nullptr, Common::PageType::Unmapped); | ||
| 565 | } else { | 515 | } else { |
| 566 | current_page_table->pointers[vaddr >> PAGE_BITS] = | 516 | current_page_table->pointers[vaddr >> PAGE_BITS].Store( |
| 567 | pointer - (vaddr & ~PAGE_MASK); | 517 | pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory); |
| 568 | page_type = Common::PageType::Memory; | ||
| 569 | } | 518 | } |
| 570 | break; | 519 | break; |
| 571 | } | 520 | } |
| @@ -595,7 +544,7 @@ struct Memory::Impl { | |||
| 595 | auto& gpu = system.GPU(); | 544 | auto& gpu = system.GPU(); |
| 596 | for (u64 i = 0; i < size; i++) { | 545 | for (u64 i = 0; i < size; i++) { |
| 597 | const auto page = base + i; | 546 | const auto page = base + i; |
| 598 | if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) { | 547 | if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) { |
| 599 | gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE); | 548 | gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE); |
| 600 | } | 549 | } |
| 601 | } | 550 | } |
| @@ -610,20 +559,18 @@ struct Memory::Impl { | |||
| 610 | "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE); | 559 | "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE); |
| 611 | 560 | ||
| 612 | while (base != end) { | 561 | while (base != end) { |
| 613 | page_table.attributes[base] = type; | 562 | page_table.pointers[base].Store(nullptr, type); |
| 614 | page_table.pointers[base] = nullptr; | ||
| 615 | page_table.backing_addr[base] = 0; | 563 | page_table.backing_addr[base] = 0; |
| 616 | 564 | ||
| 617 | base += 1; | 565 | base += 1; |
| 618 | } | 566 | } |
| 619 | } else { | 567 | } else { |
| 620 | while (base != end) { | 568 | while (base != end) { |
| 621 | page_table.pointers[base] = | 569 | page_table.pointers[base].Store( |
| 622 | system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS); | 570 | system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type); |
| 623 | page_table.attributes[base] = type; | ||
| 624 | page_table.backing_addr[base] = target - (base << PAGE_BITS); | 571 | page_table.backing_addr[base] = target - (base << PAGE_BITS); |
| 625 | 572 | ||
| 626 | ASSERT_MSG(page_table.pointers[base], | 573 | ASSERT_MSG(page_table.pointers[base].Pointer(), |
| 627 | "memory mapping base yield a nullptr within the table"); | 574 | "memory mapping base yield a nullptr within the table"); |
| 628 | 575 | ||
| 629 | base += 1; | 576 | base += 1; |
| @@ -646,21 +593,13 @@ struct Memory::Impl { | |||
| 646 | template <typename T> | 593 | template <typename T> |
| 647 | T Read(const VAddr vaddr) { | 594 | T Read(const VAddr vaddr) { |
| 648 | // Avoid adding any extra logic to this fast-path block | 595 | // Avoid adding any extra logic to this fast-path block |
| 649 | if (const u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) { | 596 | const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); |
| 597 | if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { | ||
| 650 | T value; | 598 | T value; |
| 651 | std::memcpy(&value, &pointer[vaddr], sizeof(T)); | 599 | std::memcpy(&value, &pointer[vaddr], sizeof(T)); |
| 652 | return value; | 600 | return value; |
| 653 | } | 601 | } |
| 654 | 602 | switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { | |
| 655 | // Otherwise, we need to grab the page with a lock, in case it is currently being modified | ||
| 656 | const auto entry = SafePageEntry(vaddr >> PAGE_BITS); | ||
| 657 | if (entry.pointer) { | ||
| 658 | T value; | ||
| 659 | std::memcpy(&value, &entry.pointer[vaddr], sizeof(T)); | ||
| 660 | return value; | ||
| 661 | } | ||
| 662 | |||
| 663 | switch (entry.attribute) { | ||
| 664 | case Common::PageType::Unmapped: | 603 | case Common::PageType::Unmapped: |
| 665 | LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); | 604 | LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); |
| 666 | return 0; | 605 | return 0; |
| @@ -692,20 +631,12 @@ struct Memory::Impl { | |||
| 692 | template <typename T> | 631 | template <typename T> |
| 693 | void Write(const VAddr vaddr, const T data) { | 632 | void Write(const VAddr vaddr, const T data) { |
| 694 | // Avoid adding any extra logic to this fast-path block | 633 | // Avoid adding any extra logic to this fast-path block |
| 695 | if (u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) { | 634 | const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); |
| 635 | if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { | ||
| 696 | std::memcpy(&pointer[vaddr], &data, sizeof(T)); | 636 | std::memcpy(&pointer[vaddr], &data, sizeof(T)); |
| 697 | return; | 637 | return; |
| 698 | } | 638 | } |
| 699 | 639 | switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { | |
| 700 | // Otherwise, we need to grab the page with a lock, in case it is currently being modified | ||
| 701 | const auto entry = SafePageEntry(vaddr >> PAGE_BITS); | ||
| 702 | if (entry.pointer) { | ||
| 703 | // Memory was mapped, we are done | ||
| 704 | std::memcpy(&entry.pointer[vaddr], &data, sizeof(T)); | ||
| 705 | return; | ||
| 706 | } | ||
| 707 | |||
| 708 | switch (entry.attribute) { | ||
| 709 | case Common::PageType::Unmapped: | 640 | case Common::PageType::Unmapped: |
| 710 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, | 641 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, |
| 711 | static_cast<u32>(data), vaddr); | 642 | static_cast<u32>(data), vaddr); |
| @@ -726,15 +657,13 @@ struct Memory::Impl { | |||
| 726 | 657 | ||
| 727 | template <typename T> | 658 | template <typename T> |
| 728 | bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { | 659 | bool WriteExclusive(const VAddr vaddr, const T data, const T expected) { |
| 729 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 660 | const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); |
| 730 | if (page_pointer != nullptr) { | 661 | if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { |
| 731 | // NOTE: Avoid adding any extra logic to this fast-path block | 662 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 732 | auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]); | 663 | const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]); |
| 733 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 664 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); |
| 734 | } | 665 | } |
| 735 | 666 | switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { | |
| 736 | const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||
| 737 | switch (type) { | ||
| 738 | case Common::PageType::Unmapped: | 667 | case Common::PageType::Unmapped: |
| 739 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, | 668 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, |
| 740 | static_cast<u32>(data), vaddr); | 669 | static_cast<u32>(data), vaddr); |
| @@ -755,15 +684,13 @@ struct Memory::Impl { | |||
| 755 | } | 684 | } |
| 756 | 685 | ||
| 757 | bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { | 686 | bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) { |
| 758 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 687 | const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); |
| 759 | if (page_pointer != nullptr) { | 688 | if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { |
| 760 | // NOTE: Avoid adding any extra logic to this fast-path block | 689 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 761 | auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]); | 690 | const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]); |
| 762 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 691 | return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); |
| 763 | } | 692 | } |
| 764 | 693 | switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { | |
| 765 | const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||
| 766 | switch (type) { | ||
| 767 | case Common::PageType::Unmapped: | 694 | case Common::PageType::Unmapped: |
| 768 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8, | 695 | LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8, |
| 769 | static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr); | 696 | static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr); |
| @@ -783,7 +710,6 @@ struct Memory::Impl { | |||
| 783 | return true; | 710 | return true; |
| 784 | } | 711 | } |
| 785 | 712 | ||
| 786 | mutable std::mutex rasterizer_cache_guard; | ||
| 787 | Common::PageTable* current_page_table = nullptr; | 713 | Common::PageTable* current_page_table = nullptr; |
| 788 | Core::System& system; | 714 | Core::System& system; |
| 789 | }; | 715 | }; |
| @@ -799,25 +725,10 @@ void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size | |||
| 799 | impl->MapMemoryRegion(page_table, base, size, target); | 725 | impl->MapMemoryRegion(page_table, base, size, target); |
| 800 | } | 726 | } |
| 801 | 727 | ||
| 802 | void Memory::MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 803 | Common::MemoryHookPointer mmio_handler) { | ||
| 804 | impl->MapIoRegion(page_table, base, size, std::move(mmio_handler)); | ||
| 805 | } | ||
| 806 | |||
| 807 | void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { | 728 | void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { |
| 808 | impl->UnmapRegion(page_table, base, size); | 729 | impl->UnmapRegion(page_table, base, size); |
| 809 | } | 730 | } |
| 810 | 731 | ||
| 811 | void Memory::AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 812 | Common::MemoryHookPointer hook) { | ||
| 813 | impl->AddDebugHook(page_table, base, size, std::move(hook)); | ||
| 814 | } | ||
| 815 | |||
| 816 | void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 817 | Common::MemoryHookPointer hook) { | ||
| 818 | impl->RemoveDebugHook(page_table, base, size, std::move(hook)); | ||
| 819 | } | ||
| 820 | |||
| 821 | bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { | 732 | bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { |
| 822 | return impl->IsValidVirtualAddress(process, vaddr); | 733 | return impl->IsValidVirtualAddress(process, vaddr); |
| 823 | } | 734 | } |
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 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/memory_hook.h" | ||
| 12 | 11 | ||
| 13 | namespace Common { | 12 | namespace Common { |
| 14 | struct PageTable; | 13 | struct PageTable; |
| @@ -78,17 +77,6 @@ public: | |||
| 78 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target); | 77 | void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target); |
| 79 | 78 | ||
| 80 | /** | 79 | /** |
| 81 | * Maps a region of the emulated process address space as a IO region. | ||
| 82 | * | ||
| 83 | * @param page_table The page table of the emulated process. | ||
| 84 | * @param base The address to start mapping at. Must be page-aligned. | ||
| 85 | * @param size The amount of bytes to map. Must be page-aligned. | ||
| 86 | * @param mmio_handler The handler that backs the mapping. | ||
| 87 | */ | ||
| 88 | void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 89 | Common::MemoryHookPointer mmio_handler); | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Unmaps a region of the emulated process address space. | 80 | * Unmaps a region of the emulated process address space. |
| 93 | * | 81 | * |
| 94 | * @param page_table The page table of the emulated process. | 82 | * @param page_table The page table of the emulated process. |
| @@ -98,28 +86,6 @@ public: | |||
| 98 | void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size); | 86 | void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size); |
| 99 | 87 | ||
| 100 | /** | 88 | /** |
| 101 | * Adds a memory hook to intercept reads and writes to given region of memory. | ||
| 102 | * | ||
| 103 | * @param page_table The page table of the emulated process | ||
| 104 | * @param base The starting address to apply the hook to. | ||
| 105 | * @param size The size of the memory region to apply the hook to, in bytes. | ||
| 106 | * @param hook The hook to apply to the region of memory. | ||
| 107 | */ | ||
| 108 | void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 109 | Common::MemoryHookPointer hook); | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Removes a memory hook from a given range of memory. | ||
| 113 | * | ||
| 114 | * @param page_table The page table of the emulated process. | ||
| 115 | * @param base The starting address to remove the hook from. | ||
| 116 | * @param size The size of the memory region to remove the hook from, in bytes. | ||
| 117 | * @param hook The hook to remove from the specified region of memory. | ||
| 118 | */ | ||
| 119 | void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, | ||
| 120 | Common::MemoryHookPointer hook); | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Checks whether or not the supplied address is a valid virtual | 89 | * Checks whether or not the supplied address is a valid virtual |
| 124 | * address for the given process. | 90 | * address for the given process. |
| 125 | * | 91 | * |
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) { | |||
| 148 | values.motion_enabled.SetGlobal(true); | 148 | values.motion_enabled.SetGlobal(true); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | void Sanitize() { | ||
| 152 | values.use_asynchronous_gpu_emulation.SetValue( | ||
| 153 | values.use_asynchronous_gpu_emulation.GetValue() || values.use_multi_core.GetValue()); | ||
| 154 | } | ||
| 155 | |||
| 156 | } // namespace Settings | 151 | } // 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 { | |||
| 131 | 131 | ||
| 132 | bool cpuopt_unsafe_unfuse_fma; | 132 | bool cpuopt_unsafe_unfuse_fma; |
| 133 | bool cpuopt_unsafe_reduce_fp_error; | 133 | bool cpuopt_unsafe_reduce_fp_error; |
| 134 | bool cpuopt_unsafe_inaccurate_nan; | ||
| 134 | 135 | ||
| 135 | // Renderer | 136 | // Renderer |
| 136 | Setting<RendererBackend> renderer_backend; | 137 | Setting<RendererBackend> renderer_backend; |
| @@ -221,7 +222,7 @@ struct Values { | |||
| 221 | bool disable_macro_jit; | 222 | bool disable_macro_jit; |
| 222 | bool extended_logging; | 223 | bool extended_logging; |
| 223 | 224 | ||
| 224 | // Misceallaneous | 225 | // Miscellaneous |
| 225 | std::string log_filter; | 226 | std::string log_filter; |
| 226 | bool use_dev_keys; | 227 | bool use_dev_keys; |
| 227 | 228 | ||
| @@ -257,7 +258,4 @@ void LogSettings(); | |||
| 257 | // Restore the global state of all applicable settings in the Values struct | 258 | // Restore the global state of all applicable settings in the Values struct |
| 258 | void RestoreGlobalState(bool is_powered_on); | 259 | void RestoreGlobalState(bool is_powered_on); |
| 259 | 260 | ||
| 260 | // Fixes settings that are known to cause issues with the emulator | ||
| 261 | void Sanitize(); | ||
| 262 | |||
| 263 | } // namespace Settings | 261 | } // 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: | |||
| 120 | /// For use in initialization, querying devices to find the adapter | 120 | /// For use in initialization, querying devices to find the adapter |
| 121 | void Setup(); | 121 | void Setup(); |
| 122 | 122 | ||
| 123 | /// Resets status of all GC controller devices to a disconected state | 123 | /// Resets status of all GC controller devices to a disconnected state |
| 124 | void ResetDevices(); | 124 | void ResetDevices(); |
| 125 | 125 | ||
| 126 | /// Resets status of device connected to a disconected state | 126 | /// Resets status of device connected to a disconnected state |
| 127 | void ResetDevice(std::size_t port); | 127 | void ResetDevice(std::size_t port); |
| 128 | 128 | ||
| 129 | /// Returns true if we successfully gain access to GC Adapter | 129 | /// Returns true if we successfully gain access to GC Adapter |
| 130 | bool CheckDeviceAccess(); | 130 | bool CheckDeviceAccess(); |
| 131 | 131 | ||
| 132 | /// Captures GC Adapter endpoint address | 132 | /// Captures GC Adapter endpoint address |
| 133 | /// Returns true if the endpoind was set correctly | 133 | /// Returns true if the endpoint was set correctly |
| 134 | bool GetGCEndpoint(libusb_device* device); | 134 | bool GetGCEndpoint(libusb_device* device); |
| 135 | 135 | ||
| 136 | /// For shutting down, clear all data, join all threads, release usb | 136 | /// 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) { | |||
| 129 | rad_gyro += ki * integral_error; | 129 | rad_gyro += ki * integral_error; |
| 130 | rad_gyro += kd * derivative_error; | 130 | rad_gyro += kd * derivative_error; |
| 131 | } else { | 131 | } else { |
| 132 | // Give more weight to acelerometer values to compensate for the lack of gyro | 132 | // Give more weight to accelerometer values to compensate for the lack of gyro |
| 133 | rad_gyro += 35.0f * kp * real_error; | 133 | rad_gyro += 35.0f * kp * real_error; |
| 134 | rad_gyro += 10.0f * ki * integral_error; | 134 | rad_gyro += 10.0f * ki * integral_error; |
| 135 | rad_gyro += 10.0f * kd * derivative_error; | 135 | 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 { | |||
| 20 | Left, | 20 | Left, |
| 21 | Wheel, | 21 | Wheel, |
| 22 | Right, | 22 | Right, |
| 23 | Foward, | 23 | Forward, |
| 24 | Backward, | 24 | Backward, |
| 25 | Undefined, | 25 | Undefined, |
| 26 | }; | 26 | }; |
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: | |||
| 1030 | } | 1030 | } |
| 1031 | return {}; | 1031 | return {}; |
| 1032 | } | 1032 | } |
| 1033 | [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const { | 1033 | [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(SDL_Event& event) { |
| 1034 | switch (event.type) { | 1034 | switch (event.type) { |
| 1035 | case SDL_JOYAXISMOTION: | 1035 | case SDL_JOYAXISMOTION: |
| 1036 | if (std::abs(event.jaxis.value / 32767.0) < 0.5) { | 1036 | if (!axis_memory.count(event.jaxis.which) || |
| 1037 | !axis_memory[event.jaxis.which].count(event.jaxis.axis)) { | ||
| 1038 | axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value; | ||
| 1039 | axis_event_count[event.jaxis.which][event.jaxis.axis] = 1; | ||
| 1037 | break; | 1040 | break; |
| 1041 | } else { | ||
| 1042 | axis_event_count[event.jaxis.which][event.jaxis.axis]++; | ||
| 1043 | // The joystick and axis exist in our map if we take this branch, so no checks | ||
| 1044 | // needed | ||
| 1045 | if (std::abs( | ||
| 1046 | (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) / | ||
| 1047 | 32767.0) < 0.5) { | ||
| 1048 | break; | ||
| 1049 | } else { | ||
| 1050 | if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 && | ||
| 1051 | IsAxisAtPole(event.jaxis.value) && | ||
| 1052 | IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) { | ||
| 1053 | // If we have exactly two events and both are near a pole, this is | ||
| 1054 | // likely a digital input masquerading as an analog axis; Instead of | ||
| 1055 | // trying to look at the direction the axis travelled, assume the first | ||
| 1056 | // event was press and the second was release; This should handle most | ||
| 1057 | // digital axes while deferring to the direction of travel for analog | ||
| 1058 | // axes | ||
| 1059 | event.jaxis.value = static_cast<Sint16>( | ||
| 1060 | std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis])); | ||
| 1061 | } else { | ||
| 1062 | // There are more than two events, so this is likely a true analog axis, | ||
| 1063 | // check the direction it travelled | ||
| 1064 | event.jaxis.value = static_cast<Sint16>(std::copysign( | ||
| 1065 | 32767, | ||
| 1066 | event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis])); | ||
| 1067 | } | ||
| 1068 | axis_memory.clear(); | ||
| 1069 | axis_event_count.clear(); | ||
| 1070 | } | ||
| 1038 | } | 1071 | } |
| 1039 | [[fallthrough]]; | 1072 | [[fallthrough]]; |
| 1040 | case SDL_JOYBUTTONUP: | 1073 | case SDL_JOYBUTTONUP: |
| @@ -1043,6 +1076,16 @@ public: | |||
| 1043 | } | 1076 | } |
| 1044 | return std::nullopt; | 1077 | return std::nullopt; |
| 1045 | } | 1078 | } |
| 1079 | |||
| 1080 | private: | ||
| 1081 | // Determine whether an axis value is close to an extreme or center | ||
| 1082 | // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per | ||
| 1083 | // axis, which is why the center must be considered a pole | ||
| 1084 | bool IsAxisAtPole(int16_t value) const { | ||
| 1085 | return std::abs(value) >= 32767 || std::abs(value) < 327; | ||
| 1086 | } | ||
| 1087 | std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, int16_t>> axis_memory; | ||
| 1088 | std::unordered_map<SDL_JoystickID, std::unordered_map<uint8_t, uint32_t>> axis_event_count; | ||
| 1046 | }; | 1089 | }; |
| 1047 | 1090 | ||
| 1048 | class SDLMotionPoller final : public SDLPoller { | 1091 | 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: | |||
| 28 | mutable std::mutex mutex; | 28 | mutable std::mutex mutex; |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | /// A motion device factory that creates motion devices from JC Adapter | 31 | /// A motion device factory that creates motion devices from a UDP client |
| 32 | UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) | 32 | UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) |
| 33 | : client(std::move(client_)) {} | 33 | : client(std::move(client_)) {} |
| 34 | 34 | ||
| 35 | /** | 35 | /** |
| 36 | * Creates motion device | 36 | * Creates motion device |
| 37 | * @param params contains parameters for creating the device: | 37 | * @param params contains parameters for creating the device: |
| 38 | * - "port": the nth jcpad on the adapter | 38 | * - "port": the UDP port number |
| 39 | */ | 39 | */ |
| 40 | std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { | 40 | std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { |
| 41 | auto ip = params.Get("ip", "127.0.0.1"); | 41 | auto ip = params.Get("ip", "127.0.0.1"); |
| @@ -90,14 +90,14 @@ private: | |||
| 90 | mutable std::mutex mutex; | 90 | mutable std::mutex mutex; |
| 91 | }; | 91 | }; |
| 92 | 92 | ||
| 93 | /// A motion device factory that creates motion devices from JC Adapter | 93 | /// A motion device factory that creates motion devices from a UDP client |
| 94 | UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) | 94 | UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) |
| 95 | : client(std::move(client_)) {} | 95 | : client(std::move(client_)) {} |
| 96 | 96 | ||
| 97 | /** | 97 | /** |
| 98 | * Creates motion device | 98 | * Creates motion device |
| 99 | * @param params contains parameters for creating the device: | 99 | * @param params contains parameters for creating the device: |
| 100 | * - "port": the nth jcpad on the adapter | 100 | * - "port": the UDP port number |
| 101 | */ | 101 | */ |
| 102 | std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { | 102 | std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { |
| 103 | auto ip = params.Get("ip", "127.0.0.1"); | 103 | 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 | |||
| 4 | common/fibers.cpp | 4 | common/fibers.cpp |
| 5 | common/param_package.cpp | 5 | common/param_package.cpp |
| 6 | common/ring_buffer.cpp | 6 | common/ring_buffer.cpp |
| 7 | core/arm/arm_test_common.cpp | ||
| 8 | core/arm/arm_test_common.h | ||
| 9 | core/core_timing.cpp | 7 | core/core_timing.cpp |
| 10 | tests.cpp | 8 | tests.cpp |
| 11 | ) | 9 | ) |
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) { | |||
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | /** This test checks for fiber thread exchange configuration and validates that fibers are | 209 | /** This test checks for fiber thread exchange configuration and validates that fibers are |
| 210 | * that a fiber has been succesfully transfered from one thread to another and that the TLS | 210 | * that a fiber has been successfully transferred from one thread to another and that the TLS |
| 211 | * region of the thread is kept while changing fibers. | 211 | * region of the thread is kept while changing fibers. |
| 212 | */ | 212 | */ |
| 213 | TEST_CASE("Fibers::InterExchange", "[common]") { | 213 | TEST_CASE("Fibers::InterExchange", "[common]") { |
| @@ -299,7 +299,7 @@ static void ThreadStart3(u32 id, TestControl3& test_control) { | |||
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | /** This test checks for one two threads racing for starting the same fiber. | 301 | /** This test checks for one two threads racing for starting the same fiber. |
| 302 | * It checks execution occured in an ordered manner and by no time there were | 302 | * It checks execution occurred in an ordered manner and by no time there were |
| 303 | * two contexts at the same time. | 303 | * two contexts at the same time. |
| 304 | */ | 304 | */ |
| 305 | TEST_CASE("Fibers::StartRace", "[common]") { | 305 | 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 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #include "common/page_table.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/hle/kernel/memory/page_table.h" | ||
| 10 | #include "core/hle/kernel/process.h" | ||
| 11 | #include "core/memory.h" | ||
| 12 | #include "tests/core/arm/arm_test_common.h" | ||
| 13 | |||
| 14 | namespace ArmTests { | ||
| 15 | |||
| 16 | TestEnvironment::TestEnvironment(bool mutable_memory_) | ||
| 17 | : mutable_memory(mutable_memory_), | ||
| 18 | test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} { | ||
| 19 | auto& system = Core::System::GetInstance(); | ||
| 20 | |||
| 21 | auto process = Kernel::Process::Create(system, "", Kernel::Process::ProcessType::Userland); | ||
| 22 | page_table = &process->PageTable().PageTableImpl(); | ||
| 23 | |||
| 24 | system.Memory().MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); | ||
| 25 | system.Memory().MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); | ||
| 26 | |||
| 27 | kernel.MakeCurrentProcess(process.get()); | ||
| 28 | } | ||
| 29 | |||
| 30 | TestEnvironment::~TestEnvironment() { | ||
| 31 | auto& system = Core::System::GetInstance(); | ||
| 32 | system.Memory().UnmapRegion(*page_table, 0x80000000, 0x80000000); | ||
| 33 | system.Memory().UnmapRegion(*page_table, 0x00000000, 0x80000000); | ||
| 34 | } | ||
| 35 | |||
| 36 | void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { | ||
| 37 | SetMemory32(vaddr + 0, static_cast<u32>(value)); | ||
| 38 | SetMemory32(vaddr + 4, static_cast<u32>(value >> 32)); | ||
| 39 | } | ||
| 40 | |||
| 41 | void TestEnvironment::SetMemory32(VAddr vaddr, u32 value) { | ||
| 42 | SetMemory16(vaddr + 0, static_cast<u16>(value)); | ||
| 43 | SetMemory16(vaddr + 2, static_cast<u16>(value >> 16)); | ||
| 44 | } | ||
| 45 | |||
| 46 | void TestEnvironment::SetMemory16(VAddr vaddr, u16 value) { | ||
| 47 | SetMemory8(vaddr + 0, static_cast<u8>(value)); | ||
| 48 | SetMemory8(vaddr + 1, static_cast<u8>(value >> 8)); | ||
| 49 | } | ||
| 50 | |||
| 51 | void TestEnvironment::SetMemory8(VAddr vaddr, u8 value) { | ||
| 52 | test_memory->data[vaddr] = value; | ||
| 53 | } | ||
| 54 | |||
| 55 | std::vector<WriteRecord> TestEnvironment::GetWriteRecords() const { | ||
| 56 | return write_records; | ||
| 57 | } | ||
| 58 | |||
| 59 | void TestEnvironment::ClearWriteRecords() { | ||
| 60 | write_records.clear(); | ||
| 61 | } | ||
| 62 | |||
| 63 | TestEnvironment::TestMemory::~TestMemory() {} | ||
| 64 | |||
| 65 | std::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) { | ||
| 66 | return true; | ||
| 67 | } | ||
| 68 | |||
| 69 | std::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) { | ||
| 70 | const auto iter = data.find(addr); | ||
| 71 | |||
| 72 | if (iter == data.end()) { | ||
| 73 | // Some arbitrary data | ||
| 74 | return static_cast<u8>(addr); | ||
| 75 | } | ||
| 76 | |||
| 77 | return iter->second; | ||
| 78 | } | ||
| 79 | |||
| 80 | std::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) { | ||
| 81 | return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8; | ||
| 82 | } | ||
| 83 | |||
| 84 | std::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) { | ||
| 85 | return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16; | ||
| 86 | } | ||
| 87 | |||
| 88 | std::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) { | ||
| 89 | return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; | ||
| 90 | } | ||
| 91 | |||
| 92 | bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) { | ||
| 93 | VAddr addr = src_addr; | ||
| 94 | u8* data = static_cast<u8*>(dest_buffer); | ||
| 95 | |||
| 96 | for (std::size_t i = 0; i < size; i++, addr++, data++) { | ||
| 97 | *data = *Read8(addr); | ||
| 98 | } | ||
| 99 | |||
| 100 | return true; | ||
| 101 | } | ||
| 102 | |||
| 103 | bool TestEnvironment::TestMemory::Write8(VAddr addr, u8 data) { | ||
| 104 | env->write_records.emplace_back(8, addr, data); | ||
| 105 | if (env->mutable_memory) | ||
| 106 | env->SetMemory8(addr, data); | ||
| 107 | return true; | ||
| 108 | } | ||
| 109 | |||
| 110 | bool TestEnvironment::TestMemory::Write16(VAddr addr, u16 data) { | ||
| 111 | env->write_records.emplace_back(16, addr, data); | ||
| 112 | if (env->mutable_memory) | ||
| 113 | env->SetMemory16(addr, data); | ||
| 114 | return true; | ||
| 115 | } | ||
| 116 | |||
| 117 | bool TestEnvironment::TestMemory::Write32(VAddr addr, u32 data) { | ||
| 118 | env->write_records.emplace_back(32, addr, data); | ||
| 119 | if (env->mutable_memory) | ||
| 120 | env->SetMemory32(addr, data); | ||
| 121 | return true; | ||
| 122 | } | ||
| 123 | |||
| 124 | bool TestEnvironment::TestMemory::Write64(VAddr addr, u64 data) { | ||
| 125 | env->write_records.emplace_back(64, addr, data); | ||
| 126 | if (env->mutable_memory) | ||
| 127 | env->SetMemory64(addr, data); | ||
| 128 | return true; | ||
| 129 | } | ||
| 130 | |||
| 131 | bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer, | ||
| 132 | std::size_t size) { | ||
| 133 | VAddr addr = dest_addr; | ||
| 134 | const u8* data = static_cast<const u8*>(src_buffer); | ||
| 135 | |||
| 136 | for (std::size_t i = 0; i < size; i++, addr++, data++) { | ||
| 137 | env->write_records.emplace_back(8, addr, *data); | ||
| 138 | if (env->mutable_memory) | ||
| 139 | env->SetMemory8(addr, *data); | ||
| 140 | } | ||
| 141 | |||
| 142 | return true; | ||
| 143 | } | ||
| 144 | |||
| 145 | } // 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 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <tuple> | ||
| 8 | #include <unordered_map> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/memory_hook.h" | ||
| 13 | #include "core/hle/kernel/kernel.h" | ||
| 14 | |||
| 15 | namespace Common { | ||
| 16 | struct PageTable; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace ArmTests { | ||
| 20 | |||
| 21 | struct WriteRecord { | ||
| 22 | WriteRecord(std::size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {} | ||
| 23 | std::size_t size; | ||
| 24 | VAddr addr; | ||
| 25 | u64 data; | ||
| 26 | bool operator==(const WriteRecord& o) const { | ||
| 27 | return std::tie(size, addr, data) == std::tie(o.size, o.addr, o.data); | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | class TestEnvironment final { | ||
| 32 | public: | ||
| 33 | /* | ||
| 34 | * Inititalise test environment | ||
| 35 | * @param mutable_memory If false, writes to memory can never be read back. | ||
| 36 | * (Memory is immutable.) | ||
| 37 | */ | ||
| 38 | explicit TestEnvironment(bool mutable_memory = false); | ||
| 39 | |||
| 40 | /// Shutdown test environment | ||
| 41 | ~TestEnvironment(); | ||
| 42 | |||
| 43 | /// Sets value at memory location vaddr. | ||
| 44 | void SetMemory8(VAddr vaddr, u8 value); | ||
| 45 | void SetMemory16(VAddr vaddr, u16 value); | ||
| 46 | void SetMemory32(VAddr vaddr, u32 value); | ||
| 47 | void SetMemory64(VAddr vaddr, u64 value); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Whenever Memory::Write{8,16,32,64} is called within the test environment, | ||
| 51 | * a new write-record is made. | ||
| 52 | * @returns A vector of write records made since they were last cleared. | ||
| 53 | */ | ||
| 54 | std::vector<WriteRecord> GetWriteRecords() const; | ||
| 55 | |||
| 56 | /// Empties the internal write-record store. | ||
| 57 | void ClearWriteRecords(); | ||
| 58 | |||
| 59 | private: | ||
| 60 | friend struct TestMemory; | ||
| 61 | struct TestMemory final : Common::MemoryHook { | ||
| 62 | explicit TestMemory(TestEnvironment* env_) : env(env_) {} | ||
| 63 | TestEnvironment* env; | ||
| 64 | |||
| 65 | ~TestMemory() override; | ||
| 66 | |||
| 67 | std::optional<bool> IsValidAddress(VAddr addr) override; | ||
| 68 | |||
| 69 | std::optional<u8> Read8(VAddr addr) override; | ||
| 70 | std::optional<u16> Read16(VAddr addr) override; | ||
| 71 | std::optional<u32> Read32(VAddr addr) override; | ||
| 72 | std::optional<u64> Read64(VAddr addr) override; | ||
| 73 | |||
| 74 | bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override; | ||
| 75 | |||
| 76 | bool Write8(VAddr addr, u8 data) override; | ||
| 77 | bool Write16(VAddr addr, u16 data) override; | ||
| 78 | bool Write32(VAddr addr, u32 data) override; | ||
| 79 | bool Write64(VAddr addr, u64 data) override; | ||
| 80 | |||
| 81 | bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) override; | ||
| 82 | |||
| 83 | std::unordered_map<VAddr, u8> data; | ||
| 84 | }; | ||
| 85 | |||
| 86 | bool mutable_memory; | ||
| 87 | std::shared_ptr<TestMemory> test_memory; | ||
| 88 | std::vector<WriteRecord> write_records; | ||
| 89 | Common::PageTable* page_table = nullptr; | ||
| 90 | Kernel::KernelCore kernel; | ||
| 91 | }; | ||
| 92 | |||
| 93 | } // 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 | |||
| 48 | engines/shader_bytecode.h | 48 | engines/shader_bytecode.h |
| 49 | engines/shader_header.h | 49 | engines/shader_header.h |
| 50 | engines/shader_type.h | 50 | engines/shader_type.h |
| 51 | framebuffer_config.h | ||
| 51 | macro/macro.cpp | 52 | macro/macro.cpp |
| 52 | macro/macro.h | 53 | macro/macro.h |
| 53 | macro/macro_hle.cpp | 54 | macro/macro_hle.cpp |
| @@ -59,10 +60,6 @@ add_library(video_core STATIC | |||
| 59 | fence_manager.h | 60 | fence_manager.h |
| 60 | gpu.cpp | 61 | gpu.cpp |
| 61 | gpu.h | 62 | gpu.h |
| 62 | gpu_asynch.cpp | ||
| 63 | gpu_asynch.h | ||
| 64 | gpu_synch.cpp | ||
| 65 | gpu_synch.h | ||
| 66 | gpu_thread.cpp | 63 | gpu_thread.cpp |
| 67 | gpu_thread.h | 64 | gpu_thread.h |
| 68 | guest_driver.cpp | 65 | 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) { | |||
| 53 | 53 | ||
| 54 | void Vic::Execute() { | 54 | void Vic::Execute() { |
| 55 | if (output_surface_luma_address == 0) { | 55 | if (output_surface_luma_address == 0) { |
| 56 | LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Recieved 0x{:X}", | 56 | LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Received 0x{:X}", |
| 57 | vic_state.output_surface.luma_offset); | 57 | vic_state.output_surface.luma_offset); |
| 58 | return; | 58 | return; |
| 59 | } | 59 | } |
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 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace Tegra { | ||
| 8 | |||
| 9 | /** | ||
| 10 | * Struct describing framebuffer configuration | ||
| 11 | */ | ||
| 12 | struct FramebufferConfig { | ||
| 13 | enum class PixelFormat : u32 { | ||
| 14 | A8B8G8R8_UNORM = 1, | ||
| 15 | RGB565_UNORM = 4, | ||
| 16 | B8G8R8A8_UNORM = 5, | ||
| 17 | }; | ||
| 18 | |||
| 19 | VAddr address{}; | ||
| 20 | u32 offset{}; | ||
| 21 | u32 width{}; | ||
| 22 | u32 height{}; | ||
| 23 | u32 stride{}; | ||
| 24 | PixelFormat pixel_format{}; | ||
| 25 | |||
| 26 | using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags; | ||
| 27 | TransformFlags transform_flags{}; | ||
| 28 | Common::Rectangle<int> crop_rect; | ||
| 29 | }; | ||
| 30 | |||
| 31 | } // 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 @@ | |||
| 10 | #include "core/core_timing.h" | 10 | #include "core/core_timing.h" |
| 11 | #include "core/core_timing_util.h" | 11 | #include "core/core_timing_util.h" |
| 12 | #include "core/frontend/emu_window.h" | 12 | #include "core/frontend/emu_window.h" |
| 13 | #include "core/hardware_interrupt_manager.h" | ||
| 13 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 14 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 15 | #include "video_core/engines/fermi_2d.h" | 16 | #include "video_core/engines/fermi_2d.h" |
| @@ -36,7 +37,8 @@ GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_) | |||
| 36 | kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)}, | 37 | kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)}, |
| 37 | maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)}, | 38 | maxwell_dma{std::make_unique<Engines::MaxwellDMA>(system, *memory_manager)}, |
| 38 | kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)}, | 39 | kepler_memory{std::make_unique<Engines::KeplerMemory>(system, *memory_manager)}, |
| 39 | shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_} {} | 40 | shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_}, |
| 41 | gpu_thread{system_, is_async_} {} | ||
| 40 | 42 | ||
| 41 | GPU::~GPU() = default; | 43 | GPU::~GPU() = default; |
| 42 | 44 | ||
| @@ -198,10 +200,6 @@ void GPU::SyncGuestHost() { | |||
| 198 | renderer->Rasterizer().SyncGuestHost(); | 200 | renderer->Rasterizer().SyncGuestHost(); |
| 199 | } | 201 | } |
| 200 | 202 | ||
| 201 | void GPU::OnCommandListEnd() { | ||
| 202 | renderer->Rasterizer().ReleaseFences(); | ||
| 203 | } | ||
| 204 | |||
| 205 | enum class GpuSemaphoreOperation { | 203 | enum class GpuSemaphoreOperation { |
| 206 | AcquireEqual = 0x1, | 204 | AcquireEqual = 0x1, |
| 207 | WriteLong = 0x2, | 205 | WriteLong = 0x2, |
| @@ -461,4 +459,75 @@ void GPU::ProcessSemaphoreAcquire() { | |||
| 461 | } | 459 | } |
| 462 | } | 460 | } |
| 463 | 461 | ||
| 462 | void GPU::Start() { | ||
| 463 | gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher); | ||
| 464 | cpu_context = renderer->GetRenderWindow().CreateSharedContext(); | ||
| 465 | cpu_context->MakeCurrent(); | ||
| 466 | } | ||
| 467 | |||
| 468 | void GPU::ObtainContext() { | ||
| 469 | cpu_context->MakeCurrent(); | ||
| 470 | } | ||
| 471 | |||
| 472 | void GPU::ReleaseContext() { | ||
| 473 | cpu_context->DoneCurrent(); | ||
| 474 | } | ||
| 475 | |||
| 476 | void GPU::PushGPUEntries(Tegra::CommandList&& entries) { | ||
| 477 | gpu_thread.SubmitList(std::move(entries)); | ||
| 478 | } | ||
| 479 | |||
| 480 | void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { | ||
| 481 | if (!use_nvdec) { | ||
| 482 | return; | ||
| 483 | } | ||
| 484 | // This condition fires when a video stream ends, clear all intermediary data | ||
| 485 | if (entries[0].raw == 0xDEADB33F) { | ||
| 486 | cdma_pusher.reset(); | ||
| 487 | return; | ||
| 488 | } | ||
| 489 | if (!cdma_pusher) { | ||
| 490 | cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this); | ||
| 491 | } | ||
| 492 | |||
| 493 | // SubmitCommandBuffer would make the nvdec operations async, this is not currently working | ||
| 494 | // TODO(ameerj): RE proper async nvdec operation | ||
| 495 | // gpu_thread.SubmitCommandBuffer(std::move(entries)); | ||
| 496 | |||
| 497 | cdma_pusher->Push(std::move(entries)); | ||
| 498 | cdma_pusher->DispatchCalls(); | ||
| 499 | } | ||
| 500 | |||
| 501 | void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 502 | gpu_thread.SwapBuffers(framebuffer); | ||
| 503 | } | ||
| 504 | |||
| 505 | void GPU::FlushRegion(VAddr addr, u64 size) { | ||
| 506 | gpu_thread.FlushRegion(addr, size); | ||
| 507 | } | ||
| 508 | |||
| 509 | void GPU::InvalidateRegion(VAddr addr, u64 size) { | ||
| 510 | gpu_thread.InvalidateRegion(addr, size); | ||
| 511 | } | ||
| 512 | |||
| 513 | void GPU::FlushAndInvalidateRegion(VAddr addr, u64 size) { | ||
| 514 | gpu_thread.FlushAndInvalidateRegion(addr, size); | ||
| 515 | } | ||
| 516 | |||
| 517 | void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const { | ||
| 518 | auto& interrupt_manager = system.InterruptManager(); | ||
| 519 | interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); | ||
| 520 | } | ||
| 521 | |||
| 522 | void GPU::WaitIdle() const { | ||
| 523 | gpu_thread.WaitIdle(); | ||
| 524 | } | ||
| 525 | |||
| 526 | void GPU::OnCommandListEnd() { | ||
| 527 | if (is_async) { | ||
| 528 | // This command only applies to asynchronous GPU mode | ||
| 529 | gpu_thread.OnCommandListEnd(); | ||
| 530 | } | ||
| 531 | } | ||
| 532 | |||
| 464 | } // namespace Tegra | 533 | } // 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 @@ | |||
| 15 | #include "core/hle/service/nvflinger/buffer_queue.h" | 15 | #include "core/hle/service/nvflinger/buffer_queue.h" |
| 16 | #include "video_core/cdma_pusher.h" | 16 | #include "video_core/cdma_pusher.h" |
| 17 | #include "video_core/dma_pusher.h" | 17 | #include "video_core/dma_pusher.h" |
| 18 | #include "video_core/framebuffer_config.h" | ||
| 19 | #include "video_core/gpu_thread.h" | ||
| 18 | 20 | ||
| 19 | using CacheAddr = std::uintptr_t; | 21 | using CacheAddr = std::uintptr_t; |
| 20 | [[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) { | 22 | [[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) { |
| @@ -101,28 +103,6 @@ enum class DepthFormat : u32 { | |||
| 101 | struct CommandListHeader; | 103 | struct CommandListHeader; |
| 102 | class DebugContext; | 104 | class DebugContext; |
| 103 | 105 | ||
| 104 | /** | ||
| 105 | * Struct describing framebuffer configuration | ||
| 106 | */ | ||
| 107 | struct FramebufferConfig { | ||
| 108 | enum class PixelFormat : u32 { | ||
| 109 | A8B8G8R8_UNORM = 1, | ||
| 110 | RGB565_UNORM = 4, | ||
| 111 | B8G8R8A8_UNORM = 5, | ||
| 112 | }; | ||
| 113 | |||
| 114 | VAddr address; | ||
| 115 | u32 offset; | ||
| 116 | u32 width; | ||
| 117 | u32 height; | ||
| 118 | u32 stride; | ||
| 119 | PixelFormat pixel_format; | ||
| 120 | |||
| 121 | using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags; | ||
| 122 | TransformFlags transform_flags; | ||
| 123 | Common::Rectangle<int> crop_rect; | ||
| 124 | }; | ||
| 125 | |||
| 126 | namespace Engines { | 106 | namespace Engines { |
| 127 | class Fermi2D; | 107 | class Fermi2D; |
| 128 | class Maxwell3D; | 108 | class Maxwell3D; |
| @@ -141,7 +121,7 @@ enum class EngineID { | |||
| 141 | 121 | ||
| 142 | class MemoryManager; | 122 | class MemoryManager; |
| 143 | 123 | ||
| 144 | class GPU { | 124 | class GPU final { |
| 145 | public: | 125 | public: |
| 146 | struct MethodCall { | 126 | struct MethodCall { |
| 147 | u32 method{}; | 127 | u32 method{}; |
| @@ -159,7 +139,7 @@ public: | |||
| 159 | }; | 139 | }; |
| 160 | 140 | ||
| 161 | explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_); | 141 | explicit GPU(Core::System& system_, bool is_async_, bool use_nvdec_); |
| 162 | virtual ~GPU(); | 142 | ~GPU(); |
| 163 | 143 | ||
| 164 | /// Binds a renderer to the GPU. | 144 | /// Binds a renderer to the GPU. |
| 165 | void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer); | 145 | void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer); |
| @@ -176,7 +156,7 @@ public: | |||
| 176 | /// Synchronizes CPU writes with Host GPU memory. | 156 | /// Synchronizes CPU writes with Host GPU memory. |
| 177 | void SyncGuestHost(); | 157 | void SyncGuestHost(); |
| 178 | /// Signal the ending of command list. | 158 | /// Signal the ending of command list. |
| 179 | virtual void OnCommandListEnd(); | 159 | void OnCommandListEnd(); |
| 180 | 160 | ||
| 181 | /// Request a host GPU memory flush from the CPU. | 161 | /// Request a host GPU memory flush from the CPU. |
| 182 | [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); | 162 | [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size); |
| @@ -240,7 +220,7 @@ public: | |||
| 240 | } | 220 | } |
| 241 | 221 | ||
| 242 | // Waits for the GPU to finish working | 222 | // Waits for the GPU to finish working |
| 243 | virtual void WaitIdle() const = 0; | 223 | void WaitIdle() const; |
| 244 | 224 | ||
| 245 | /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. | 225 | /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. |
| 246 | void WaitFence(u32 syncpoint_id, u32 value); | 226 | void WaitFence(u32 syncpoint_id, u32 value); |
| @@ -330,34 +310,34 @@ public: | |||
| 330 | /// Performs any additional setup necessary in order to begin GPU emulation. | 310 | /// Performs any additional setup necessary in order to begin GPU emulation. |
| 331 | /// This can be used to launch any necessary threads and register any necessary | 311 | /// This can be used to launch any necessary threads and register any necessary |
| 332 | /// core timing events. | 312 | /// core timing events. |
| 333 | virtual void Start() = 0; | 313 | void Start(); |
| 334 | 314 | ||
| 335 | /// Obtain the CPU Context | 315 | /// Obtain the CPU Context |
| 336 | virtual void ObtainContext() = 0; | 316 | void ObtainContext(); |
| 337 | 317 | ||
| 338 | /// Release the CPU Context | 318 | /// Release the CPU Context |
| 339 | virtual void ReleaseContext() = 0; | 319 | void ReleaseContext(); |
| 340 | 320 | ||
| 341 | /// Push GPU command entries to be processed | 321 | /// Push GPU command entries to be processed |
| 342 | virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; | 322 | void PushGPUEntries(Tegra::CommandList&& entries); |
| 343 | 323 | ||
| 344 | /// Push GPU command buffer entries to be processed | 324 | /// Push GPU command buffer entries to be processed |
| 345 | virtual void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) = 0; | 325 | void PushCommandBuffer(Tegra::ChCommandHeaderList& entries); |
| 346 | 326 | ||
| 347 | /// Swap buffers (render frame) | 327 | /// Swap buffers (render frame) |
| 348 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; | 328 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); |
| 349 | 329 | ||
| 350 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 330 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 351 | virtual void FlushRegion(VAddr addr, u64 size) = 0; | 331 | void FlushRegion(VAddr addr, u64 size); |
| 352 | 332 | ||
| 353 | /// Notify rasterizer that any caches of the specified region should be invalidated | 333 | /// Notify rasterizer that any caches of the specified region should be invalidated |
| 354 | virtual void InvalidateRegion(VAddr addr, u64 size) = 0; | 334 | void InvalidateRegion(VAddr addr, u64 size); |
| 355 | 335 | ||
| 356 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | 336 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated |
| 357 | virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; | 337 | void FlushAndInvalidateRegion(VAddr addr, u64 size); |
| 358 | 338 | ||
| 359 | protected: | 339 | protected: |
| 360 | virtual void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const = 0; | 340 | void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const; |
| 361 | 341 | ||
| 362 | private: | 342 | private: |
| 363 | void ProcessBindMethod(const MethodCall& method_call); | 343 | void ProcessBindMethod(const MethodCall& method_call); |
| @@ -427,6 +407,9 @@ private: | |||
| 427 | std::mutex flush_request_mutex; | 407 | std::mutex flush_request_mutex; |
| 428 | 408 | ||
| 429 | const bool is_async; | 409 | const bool is_async; |
| 410 | |||
| 411 | VideoCommon::GPUThread::ThreadManager gpu_thread; | ||
| 412 | std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; | ||
| 430 | }; | 413 | }; |
| 431 | 414 | ||
| 432 | #define ASSERT_REG_POSITION(field_name, position) \ | 415 | #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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hardware_interrupt_manager.h" | ||
| 7 | #include "video_core/gpu_asynch.h" | ||
| 8 | #include "video_core/gpu_thread.h" | ||
| 9 | #include "video_core/renderer_base.h" | ||
| 10 | |||
| 11 | namespace VideoCommon { | ||
| 12 | |||
| 13 | GPUAsynch::GPUAsynch(Core::System& system_, bool use_nvdec_) | ||
| 14 | : GPU{system_, true, use_nvdec_}, gpu_thread{system_} {} | ||
| 15 | |||
| 16 | GPUAsynch::~GPUAsynch() = default; | ||
| 17 | |||
| 18 | void GPUAsynch::Start() { | ||
| 19 | gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher); | ||
| 20 | cpu_context = renderer->GetRenderWindow().CreateSharedContext(); | ||
| 21 | cpu_context->MakeCurrent(); | ||
| 22 | } | ||
| 23 | |||
| 24 | void GPUAsynch::ObtainContext() { | ||
| 25 | cpu_context->MakeCurrent(); | ||
| 26 | } | ||
| 27 | |||
| 28 | void GPUAsynch::ReleaseContext() { | ||
| 29 | cpu_context->DoneCurrent(); | ||
| 30 | } | ||
| 31 | |||
| 32 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { | ||
| 33 | gpu_thread.SubmitList(std::move(entries)); | ||
| 34 | } | ||
| 35 | |||
| 36 | void GPUAsynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { | ||
| 37 | if (!use_nvdec) { | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | // This condition fires when a video stream ends, clear all intermediary data | ||
| 41 | if (entries[0].raw == 0xDEADB33F) { | ||
| 42 | cdma_pusher.reset(); | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | if (!cdma_pusher) { | ||
| 46 | cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this); | ||
| 47 | } | ||
| 48 | |||
| 49 | // SubmitCommandBuffer would make the nvdec operations async, this is not currently working | ||
| 50 | // TODO(ameerj): RE proper async nvdec operation | ||
| 51 | // gpu_thread.SubmitCommandBuffer(std::move(entries)); | ||
| 52 | |||
| 53 | cdma_pusher->Push(std::move(entries)); | ||
| 54 | cdma_pusher->DispatchCalls(); | ||
| 55 | } | ||
| 56 | |||
| 57 | void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 58 | gpu_thread.SwapBuffers(framebuffer); | ||
| 59 | } | ||
| 60 | |||
| 61 | void GPUAsynch::FlushRegion(VAddr addr, u64 size) { | ||
| 62 | gpu_thread.FlushRegion(addr, size); | ||
| 63 | } | ||
| 64 | |||
| 65 | void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) { | ||
| 66 | gpu_thread.InvalidateRegion(addr, size); | ||
| 67 | } | ||
| 68 | |||
| 69 | void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { | ||
| 70 | gpu_thread.FlushAndInvalidateRegion(addr, size); | ||
| 71 | } | ||
| 72 | |||
| 73 | void GPUAsynch::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const { | ||
| 74 | auto& interrupt_manager = system.InterruptManager(); | ||
| 75 | interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); | ||
| 76 | } | ||
| 77 | |||
| 78 | void GPUAsynch::WaitIdle() const { | ||
| 79 | gpu_thread.WaitIdle(); | ||
| 80 | } | ||
| 81 | |||
| 82 | void GPUAsynch::OnCommandListEnd() { | ||
| 83 | gpu_thread.OnCommandListEnd(); | ||
| 84 | } | ||
| 85 | |||
| 86 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "video_core/gpu.h" | ||
| 8 | #include "video_core/gpu_thread.h" | ||
| 9 | |||
| 10 | namespace Core::Frontend { | ||
| 11 | class GraphicsContext; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace VideoCore { | ||
| 15 | class RendererBase; | ||
| 16 | } // namespace VideoCore | ||
| 17 | |||
| 18 | namespace VideoCommon { | ||
| 19 | |||
| 20 | /// Implementation of GPU interface that runs the GPU asynchronously | ||
| 21 | class GPUAsynch final : public Tegra::GPU { | ||
| 22 | public: | ||
| 23 | explicit GPUAsynch(Core::System& system_, bool use_nvdec_); | ||
| 24 | ~GPUAsynch() override; | ||
| 25 | |||
| 26 | void Start() override; | ||
| 27 | void ObtainContext() override; | ||
| 28 | void ReleaseContext() override; | ||
| 29 | void PushGPUEntries(Tegra::CommandList&& entries) override; | ||
| 30 | void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override; | ||
| 31 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | ||
| 32 | void FlushRegion(VAddr addr, u64 size) override; | ||
| 33 | void InvalidateRegion(VAddr addr, u64 size) override; | ||
| 34 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | ||
| 35 | void WaitIdle() const override; | ||
| 36 | |||
| 37 | void OnCommandListEnd() override; | ||
| 38 | |||
| 39 | protected: | ||
| 40 | void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override; | ||
| 41 | |||
| 42 | private: | ||
| 43 | GPUThread::ThreadManager gpu_thread; | ||
| 44 | std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "video_core/gpu_synch.h" | ||
| 6 | #include "video_core/renderer_base.h" | ||
| 7 | |||
| 8 | namespace VideoCommon { | ||
| 9 | |||
| 10 | GPUSynch::GPUSynch(Core::System& system_, bool use_nvdec_) : GPU{system_, false, use_nvdec_} {} | ||
| 11 | |||
| 12 | GPUSynch::~GPUSynch() = default; | ||
| 13 | |||
| 14 | void GPUSynch::Start() {} | ||
| 15 | |||
| 16 | void GPUSynch::ObtainContext() { | ||
| 17 | renderer->Context().MakeCurrent(); | ||
| 18 | } | ||
| 19 | |||
| 20 | void GPUSynch::ReleaseContext() { | ||
| 21 | renderer->Context().DoneCurrent(); | ||
| 22 | } | ||
| 23 | |||
| 24 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { | ||
| 25 | dma_pusher->Push(std::move(entries)); | ||
| 26 | dma_pusher->DispatchCalls(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void GPUSynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) { | ||
| 30 | if (!use_nvdec) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | // This condition fires when a video stream ends, clears all intermediary data | ||
| 34 | if (entries[0].raw == 0xDEADB33F) { | ||
| 35 | cdma_pusher.reset(); | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | if (!cdma_pusher) { | ||
| 39 | cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this); | ||
| 40 | } | ||
| 41 | cdma_pusher->Push(std::move(entries)); | ||
| 42 | cdma_pusher->DispatchCalls(); | ||
| 43 | } | ||
| 44 | |||
| 45 | void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 46 | renderer->SwapBuffers(framebuffer); | ||
| 47 | } | ||
| 48 | |||
| 49 | void GPUSynch::FlushRegion(VAddr addr, u64 size) { | ||
| 50 | renderer->Rasterizer().FlushRegion(addr, size); | ||
| 51 | } | ||
| 52 | |||
| 53 | void GPUSynch::InvalidateRegion(VAddr addr, u64 size) { | ||
| 54 | renderer->Rasterizer().InvalidateRegion(addr, size); | ||
| 55 | } | ||
| 56 | |||
| 57 | void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { | ||
| 58 | renderer->Rasterizer().FlushAndInvalidateRegion(addr, size); | ||
| 59 | } | ||
| 60 | |||
| 61 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "video_core/gpu.h" | ||
| 8 | |||
| 9 | namespace Core::Frontend { | ||
| 10 | class GraphicsContext; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace VideoCore { | ||
| 14 | class RendererBase; | ||
| 15 | } // namespace VideoCore | ||
| 16 | |||
| 17 | namespace VideoCommon { | ||
| 18 | |||
| 19 | /// Implementation of GPU interface that runs the GPU synchronously | ||
| 20 | class GPUSynch final : public Tegra::GPU { | ||
| 21 | public: | ||
| 22 | explicit GPUSynch(Core::System& system_, bool use_nvdec_); | ||
| 23 | ~GPUSynch() override; | ||
| 24 | |||
| 25 | void Start() override; | ||
| 26 | void ObtainContext() override; | ||
| 27 | void ReleaseContext() override; | ||
| 28 | void PushGPUEntries(Tegra::CommandList&& entries) override; | ||
| 29 | void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override; | ||
| 30 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | ||
| 31 | void FlushRegion(VAddr addr, u64 size) override; | ||
| 32 | void InvalidateRegion(VAddr addr, u64 size) override; | ||
| 33 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | ||
| 34 | void WaitIdle() const override {} | ||
| 35 | |||
| 36 | protected: | ||
| 37 | void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, | ||
| 38 | [[maybe_unused]] u32 value) const override {} | ||
| 39 | }; | ||
| 40 | |||
| 41 | } // 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 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "common/scope_exit.h" | ||
| 7 | #include "common/thread.h" | 8 | #include "common/thread.h" |
| 8 | #include "core/core.h" | 9 | #include "core/core.h" |
| 9 | #include "core/frontend/emu_window.h" | 10 | #include "core/frontend/emu_window.h" |
| @@ -21,6 +22,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, | |||
| 21 | SynchState& state, Tegra::CDmaPusher& cdma_pusher) { | 22 | SynchState& state, Tegra::CDmaPusher& cdma_pusher) { |
| 22 | std::string name = "yuzu:GPU"; | 23 | std::string name = "yuzu:GPU"; |
| 23 | MicroProfileOnThreadCreate(name.c_str()); | 24 | MicroProfileOnThreadCreate(name.c_str()); |
| 25 | SCOPE_EXIT({ MicroProfileOnThreadExit(); }); | ||
| 26 | |||
| 24 | Common::SetCurrentThreadName(name.c_str()); | 27 | Common::SetCurrentThreadName(name.c_str()); |
| 25 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); | 28 | Common::SetCurrentThreadPriority(Common::ThreadPriority::High); |
| 26 | system.RegisterHostThread(); | 29 | system.RegisterHostThread(); |
| @@ -65,7 +68,8 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, | |||
| 65 | } | 68 | } |
| 66 | } | 69 | } |
| 67 | 70 | ||
| 68 | ThreadManager::ThreadManager(Core::System& system_) : system{system_} {} | 71 | ThreadManager::ThreadManager(Core::System& system_, bool is_async_) |
| 72 | : system{system_}, is_async{is_async_} {} | ||
| 69 | 73 | ||
| 70 | ThreadManager::~ThreadManager() { | 74 | ThreadManager::~ThreadManager() { |
| 71 | if (!thread.joinable()) { | 75 | if (!thread.joinable()) { |
| @@ -97,19 +101,30 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 97 | } | 101 | } |
| 98 | 102 | ||
| 99 | void ThreadManager::FlushRegion(VAddr addr, u64 size) { | 103 | void ThreadManager::FlushRegion(VAddr addr, u64 size) { |
| 100 | if (!Settings::IsGPULevelHigh()) { | 104 | if (!is_async) { |
| 105 | // Always flush with synchronous GPU mode | ||
| 101 | PushCommand(FlushRegionCommand(addr, size)); | 106 | PushCommand(FlushRegionCommand(addr, size)); |
| 102 | return; | 107 | return; |
| 103 | } | 108 | } |
| 104 | if (!Settings::IsGPULevelExtreme()) { | 109 | |
| 105 | return; | 110 | // Asynchronous GPU mode |
| 106 | } | 111 | switch (Settings::values.gpu_accuracy.GetValue()) { |
| 107 | if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) { | 112 | case Settings::GPUAccuracy::Normal: |
| 113 | PushCommand(FlushRegionCommand(addr, size)); | ||
| 114 | break; | ||
| 115 | case Settings::GPUAccuracy::High: | ||
| 116 | // TODO(bunnei): Is this right? Preserving existing behavior for now | ||
| 117 | break; | ||
| 118 | case Settings::GPUAccuracy::Extreme: { | ||
| 108 | auto& gpu = system.GPU(); | 119 | auto& gpu = system.GPU(); |
| 109 | u64 fence = gpu.RequestFlush(addr, size); | 120 | u64 fence = gpu.RequestFlush(addr, size); |
| 110 | PushCommand(GPUTickCommand()); | 121 | PushCommand(GPUTickCommand()); |
| 111 | while (fence > gpu.CurrentFlushRequestFence()) { | 122 | while (fence > gpu.CurrentFlushRequestFence()) { |
| 112 | } | 123 | } |
| 124 | break; | ||
| 125 | } | ||
| 126 | default: | ||
| 127 | UNIMPLEMENTED_MSG("Unsupported gpu_accuracy {}", Settings::values.gpu_accuracy.GetValue()); | ||
| 113 | } | 128 | } |
| 114 | } | 129 | } |
| 115 | 130 | ||
| @@ -123,7 +138,8 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { | |||
| 123 | } | 138 | } |
| 124 | 139 | ||
| 125 | void ThreadManager::WaitIdle() const { | 140 | void ThreadManager::WaitIdle() const { |
| 126 | while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed)) { | 141 | while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed) && |
| 142 | system.IsPoweredOn()) { | ||
| 127 | } | 143 | } |
| 128 | } | 144 | } |
| 129 | 145 | ||
| @@ -134,6 +150,12 @@ void ThreadManager::OnCommandListEnd() { | |||
| 134 | u64 ThreadManager::PushCommand(CommandData&& command_data) { | 150 | u64 ThreadManager::PushCommand(CommandData&& command_data) { |
| 135 | const u64 fence{++state.last_fence}; | 151 | const u64 fence{++state.last_fence}; |
| 136 | state.queue.Push(CommandDataContainer(std::move(command_data), fence)); | 152 | state.queue.Push(CommandDataContainer(std::move(command_data), fence)); |
| 153 | |||
| 154 | if (!is_async) { | ||
| 155 | // In synchronous GPU mode, block the caller until the command has executed | ||
| 156 | WaitIdle(); | ||
| 157 | } | ||
| 158 | |||
| 137 | return fence; | 159 | return fence; |
| 138 | } | 160 | } |
| 139 | 161 | ||
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 @@ | |||
| 10 | #include <optional> | 10 | #include <optional> |
| 11 | #include <thread> | 11 | #include <thread> |
| 12 | #include <variant> | 12 | #include <variant> |
| 13 | |||
| 13 | #include "common/threadsafe_queue.h" | 14 | #include "common/threadsafe_queue.h" |
| 14 | #include "video_core/gpu.h" | 15 | #include "video_core/framebuffer_config.h" |
| 15 | 16 | ||
| 16 | namespace Tegra { | 17 | namespace Tegra { |
| 17 | struct FramebufferConfig; | 18 | struct FramebufferConfig; |
| @@ -25,6 +26,10 @@ class GraphicsContext; | |||
| 25 | class System; | 26 | class System; |
| 26 | } // namespace Core | 27 | } // namespace Core |
| 27 | 28 | ||
| 29 | namespace VideoCore { | ||
| 30 | class RendererBase; | ||
| 31 | } // namespace VideoCore | ||
| 32 | |||
| 28 | namespace VideoCommon::GPUThread { | 33 | namespace VideoCommon::GPUThread { |
| 29 | 34 | ||
| 30 | /// Command to signal to the GPU thread that processing has ended | 35 | /// Command to signal to the GPU thread that processing has ended |
| @@ -112,7 +117,7 @@ struct SynchState final { | |||
| 112 | /// Class used to manage the GPU thread | 117 | /// Class used to manage the GPU thread |
| 113 | class ThreadManager final { | 118 | class ThreadManager final { |
| 114 | public: | 119 | public: |
| 115 | explicit ThreadManager(Core::System& system_); | 120 | explicit ThreadManager(Core::System& system_, bool is_async_); |
| 116 | ~ThreadManager(); | 121 | ~ThreadManager(); |
| 117 | 122 | ||
| 118 | /// Creates and starts the GPU thread. | 123 | /// Creates and starts the GPU thread. |
| @@ -150,6 +155,7 @@ private: | |||
| 150 | Core::System& system; | 155 | Core::System& system; |
| 151 | std::thread thread; | 156 | std::thread thread; |
| 152 | std::thread::id thread_id; | 157 | std::thread::id thread_id; |
| 158 | const bool is_async; | ||
| 153 | }; | 159 | }; |
| 154 | 160 | ||
| 155 | } // namespace VideoCommon::GPUThread | 161 | } // 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 | |||
| 484 | } | 484 | } |
| 485 | 485 | ||
| 486 | void VKDevice::ReportLoss() const { | 486 | void VKDevice::ReportLoss() const { |
| 487 | LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); | 487 | LOG_CRITICAL(Render_Vulkan, "Device loss occurred!"); |
| 488 | 488 | ||
| 489 | // Wait for the log to flush and for Nsight Aftermath to dump the results | 489 | // Wait for the log to flush and for Nsight Aftermath to dump the results |
| 490 | std::this_thread::sleep_for(std::chrono::seconds{15}); | 490 | 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 @@ | |||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/settings.h" | 9 | #include "core/settings.h" |
| 10 | #include "video_core/gpu_asynch.h" | ||
| 11 | #include "video_core/gpu_synch.h" | ||
| 12 | #include "video_core/renderer_base.h" | 10 | #include "video_core/renderer_base.h" |
| 13 | #include "video_core/renderer_opengl/renderer_opengl.h" | 11 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 14 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 12 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| @@ -39,13 +37,9 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer( | |||
| 39 | namespace VideoCore { | 37 | namespace VideoCore { |
| 40 | 38 | ||
| 41 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { | 39 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { |
| 42 | std::unique_ptr<Tegra::GPU> gpu; | ||
| 43 | const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue(); | 40 | const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue(); |
| 44 | if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) { | 41 | std::unique_ptr<Tegra::GPU> gpu = std::make_unique<Tegra::GPU>( |
| 45 | gpu = std::make_unique<VideoCommon::GPUAsynch>(system, use_nvdec); | 42 | system, Settings::values.use_asynchronous_gpu_emulation.GetValue(), use_nvdec); |
| 46 | } else { | ||
| 47 | gpu = std::make_unique<VideoCommon::GPUSynch>(system, use_nvdec); | ||
| 48 | } | ||
| 49 | 43 | ||
| 50 | auto context = emu_window.CreateSharedContext(); | 44 | auto context = emu_window.CreateSharedContext(); |
| 51 | const auto scope = context->Acquire(); | 45 | 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) | |||
| 535 | // This emulates a delay between disconnecting and reconnecting controllers as some games | 535 | // This emulates a delay between disconnecting and reconnecting controllers as some games |
| 536 | // do not respond to a change in controller type if it was instantaneous. | 536 | // do not respond to a change in controller type if it was instantaneous. |
| 537 | using namespace std::chrono_literals; | 537 | using namespace std::chrono_literals; |
| 538 | std::this_thread::sleep_for(20ms); | 538 | std::this_thread::sleep_for(60ms); |
| 539 | 539 | ||
| 540 | UpdateController(controller_type, player_index, player_connected); | 540 | UpdateController(controller_type, player_index, player_connected); |
| 541 | } | 541 | } |
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; | |||
| 19 | void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { | 19 | void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { |
| 20 | callback = std::move(finished); | 20 | callback = std::move(finished); |
| 21 | emit MainWindowDisplayError( | 21 | emit MainWindowDisplayError( |
| 22 | tr("An error has occured.\nPlease try again or contact the developer of the " | 22 | tr("An error has occurred.\nPlease try again or contact the developer of the " |
| 23 | "software.\n\nError Code: %1-%2 (0x%3)") | 23 | "software.\n\nError Code: %1-%2 (0x%3)") |
| 24 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) | 24 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) |
| 25 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) | 25 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) |
| @@ -32,7 +32,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon | |||
| 32 | 32 | ||
| 33 | const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); | 33 | const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); |
| 34 | emit MainWindowDisplayError( | 34 | emit MainWindowDisplayError( |
| 35 | tr("An error occured on %1 at %2.\nPlease try again or contact the " | 35 | tr("An error occurred on %1 at %2.\nPlease try again or contact the " |
| 36 | "developer of the software.\n\nError Code: %3-%4 (0x%5)") | 36 | "developer of the software.\n\nError Code: %3-%4 (0x%5)") |
| 37 | .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) | 37 | .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) |
| 38 | .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) | 38 | .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) |
| @@ -46,7 +46,7 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te | |||
| 46 | std::function<void()> finished) const { | 46 | std::function<void()> finished) const { |
| 47 | callback = std::move(finished); | 47 | callback = std::move(finished); |
| 48 | emit MainWindowDisplayError( | 48 | emit MainWindowDisplayError( |
| 49 | tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") | 49 | tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") |
| 50 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) | 50 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) |
| 51 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) | 51 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) |
| 52 | .arg(error.raw, 8, 16, QChar::fromLatin1('0')) | 52 | .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() { | |||
| 72 | void CompatDB::OnTestcaseSubmitted() { | 72 | void CompatDB::OnTestcaseSubmitted() { |
| 73 | if (!testcase_watcher.result()) { | 73 | if (!testcase_watcher.result()) { |
| 74 | QMessageBox::critical(this, tr("Communication error"), | 74 | QMessageBox::critical(this, tr("Communication error"), |
| 75 | tr("An error occured while sending the Testcase")); | 75 | tr("An error occurred while sending the Testcase")); |
| 76 | button(NextButton)->setEnabled(true); | 76 | button(NextButton)->setEnabled(true); |
| 77 | button(NextButton)->setText(tr("Next")); | 77 | button(NextButton)->setText(tr("Next")); |
| 78 | button(CancelButton)->setVisible(true); | 78 | 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() { | |||
| 764 | ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool(); | 764 | ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool(); |
| 765 | Settings::values.cpuopt_unsafe_reduce_fp_error = | 765 | Settings::values.cpuopt_unsafe_reduce_fp_error = |
| 766 | ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); | 766 | ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); |
| 767 | Settings::values.cpuopt_unsafe_inaccurate_nan = | ||
| 768 | ReadSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true).toBool(); | ||
| 767 | } | 769 | } |
| 768 | 770 | ||
| 769 | qt_config->endGroup(); | 771 | qt_config->endGroup(); |
| @@ -1327,6 +1329,8 @@ void Config::SaveCpuValues() { | |||
| 1327 | Settings::values.cpuopt_unsafe_unfuse_fma, true); | 1329 | Settings::values.cpuopt_unsafe_unfuse_fma, true); |
| 1328 | WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), | 1330 | WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), |
| 1329 | Settings::values.cpuopt_unsafe_reduce_fp_error, true); | 1331 | Settings::values.cpuopt_unsafe_reduce_fp_error, true); |
| 1332 | WriteSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), | ||
| 1333 | Settings::values.cpuopt_unsafe_inaccurate_nan, true); | ||
| 1330 | } | 1334 | } |
| 1331 | 1335 | ||
| 1332 | qt_config->endGroup(); | 1336 | qt_config->endGroup(); |
| @@ -1589,14 +1593,12 @@ void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool | |||
| 1589 | 1593 | ||
| 1590 | void Config::Reload() { | 1594 | void Config::Reload() { |
| 1591 | ReadValues(); | 1595 | ReadValues(); |
| 1592 | Settings::Sanitize(); | ||
| 1593 | // To apply default value changes | 1596 | // To apply default value changes |
| 1594 | SaveValues(); | 1597 | SaveValues(); |
| 1595 | Settings::Apply(Core::System::GetInstance()); | 1598 | Settings::Apply(Core::System::GetInstance()); |
| 1596 | } | 1599 | } |
| 1597 | 1600 | ||
| 1598 | void Config::Save() { | 1601 | void Config::Save() { |
| 1599 | Settings::Sanitize(); | ||
| 1600 | SaveValues(); | 1602 | SaveValues(); |
| 1601 | } | 1603 | } |
| 1602 | 1604 | ||
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() { | |||
| 36 | ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma); | 36 | ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma); |
| 37 | ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); | 37 | ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); |
| 38 | ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); | 38 | ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); |
| 39 | ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); | ||
| 40 | ui->cpuopt_unsafe_inaccurate_nan->setChecked(Settings::values.cpuopt_unsafe_inaccurate_nan); | ||
| 39 | } | 41 | } |
| 40 | 42 | ||
| 41 | void ConfigureCpu::AccuracyUpdated(int index) { | 43 | void ConfigureCpu::AccuracyUpdated(int index) { |
| @@ -61,6 +63,7 @@ void ConfigureCpu::ApplyConfiguration() { | |||
| 61 | static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); | 63 | static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); |
| 62 | Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked(); | 64 | Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked(); |
| 63 | Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); | 65 | Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); |
| 66 | Settings::values.cpuopt_unsafe_inaccurate_nan = ui->cpuopt_unsafe_inaccurate_nan->isChecked(); | ||
| 64 | } | 67 | } |
| 65 | 68 | ||
| 66 | void ConfigureCpu::changeEvent(QEvent* event) { | 69 | 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 @@ | |||
| 109 | </property> | 109 | </property> |
| 110 | </widget> | 110 | </widget> |
| 111 | </item> | 111 | </item> |
| 112 | <item> | ||
| 113 | <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan"> | ||
| 114 | <property name="text"> | ||
| 115 | <string>Inaccurate NaN handling</string> | ||
| 116 | </property> | ||
| 117 | <property name="toolTip"> | ||
| 118 | <string> | ||
| 119 | <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> | ||
| 120 | </string> | ||
| 121 | </property> | ||
| 122 | </widget> | ||
| 123 | </item> | ||
| 112 | </layout> | 124 | </layout> |
| 113 | </widget> | 125 | </widget> |
| 114 | </item> | 126 | </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 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <thread> | ||
| 7 | 8 | ||
| 8 | #include <QSignalBlocker> | 9 | #include <QSignalBlocker> |
| 9 | #include <QTimer> | 10 | #include <QTimer> |
| @@ -181,8 +182,18 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const { | |||
| 181 | } | 182 | } |
| 182 | 183 | ||
| 183 | void ConfigureInput::ApplyConfiguration() { | 184 | void ConfigureInput::ApplyConfiguration() { |
| 184 | for (auto controller : player_controllers) { | 185 | for (auto* controller : player_controllers) { |
| 185 | controller->ApplyConfiguration(); | 186 | controller->ApplyConfiguration(); |
| 187 | controller->TryDisconnectSelectedController(); | ||
| 188 | } | ||
| 189 | |||
| 190 | // This emulates a delay between disconnecting and reconnecting controllers as some games | ||
| 191 | // do not respond to a change in controller type if it was instantaneous. | ||
| 192 | using namespace std::chrono_literals; | ||
| 193 | std::this_thread::sleep_for(60ms); | ||
| 194 | |||
| 195 | for (auto* controller : player_controllers) { | ||
| 196 | controller->TryConnectSelectedController(); | ||
| 186 | } | 197 | } |
| 187 | 198 | ||
| 188 | advanced->ApplyConfiguration(); | 199 | 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 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <thread> | ||
| 8 | #include <utility> | 7 | #include <utility> |
| 9 | #include <QGridLayout> | 8 | #include <QGridLayout> |
| 10 | #include <QInputDialog> | 9 | #include <QInputDialog> |
| @@ -576,6 +575,10 @@ void ConfigureInputPlayer::ApplyConfiguration() { | |||
| 576 | 575 | ||
| 577 | std::transform(motions_param.begin(), motions_param.end(), motions.begin(), | 576 | std::transform(motions_param.begin(), motions_param.end(), motions.begin(), |
| 578 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | 577 | [](const Common::ParamPackage& param) { return param.Serialize(); }); |
| 578 | } | ||
| 579 | |||
| 580 | void ConfigureInputPlayer::TryConnectSelectedController() { | ||
| 581 | auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 579 | 582 | ||
| 580 | const auto controller_type = | 583 | const auto controller_type = |
| 581 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | 584 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); |
| @@ -588,15 +591,12 @@ void ConfigureInputPlayer::ApplyConfiguration() { | |||
| 588 | return; | 591 | return; |
| 589 | } | 592 | } |
| 590 | 593 | ||
| 591 | // Disconnect the controller first. | ||
| 592 | UpdateController(controller_type, player_index, false); | ||
| 593 | |||
| 594 | player.controller_type = controller_type; | 594 | player.controller_type = controller_type; |
| 595 | player.connected = player_connected; | 595 | player.connected = player_connected; |
| 596 | 596 | ||
| 597 | ConfigureVibration::SetVibrationDevices(player_index); | 597 | ConfigureVibration::SetVibrationDevices(player_index); |
| 598 | 598 | ||
| 599 | // Handheld | 599 | // Connect/Disconnect Handheld depending on Player 1's controller configuration. |
| 600 | if (player_index == 0) { | 600 | if (player_index == 0) { |
| 601 | auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; | 601 | auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; |
| 602 | if (controller_type == Settings::ControllerType::Handheld) { | 602 | if (controller_type == Settings::ControllerType::Handheld) { |
| @@ -611,14 +611,26 @@ void ConfigureInputPlayer::ApplyConfiguration() { | |||
| 611 | return; | 611 | return; |
| 612 | } | 612 | } |
| 613 | 613 | ||
| 614 | // This emulates a delay between disconnecting and reconnecting controllers as some games | ||
| 615 | // do not respond to a change in controller type if it was instantaneous. | ||
| 616 | using namespace std::chrono_literals; | ||
| 617 | std::this_thread::sleep_for(20ms); | ||
| 618 | |||
| 619 | UpdateController(controller_type, player_index, player_connected); | 614 | UpdateController(controller_type, player_index, player_connected); |
| 620 | } | 615 | } |
| 621 | 616 | ||
| 617 | void ConfigureInputPlayer::TryDisconnectSelectedController() { | ||
| 618 | const auto& player = Settings::values.players.GetValue()[player_index]; | ||
| 619 | |||
| 620 | const auto controller_type = | ||
| 621 | GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); | ||
| 622 | const auto player_connected = ui->groupConnectedController->isChecked() && | ||
| 623 | controller_type != Settings::ControllerType::Handheld; | ||
| 624 | |||
| 625 | // Do not do anything if the controller configuration has not changed. | ||
| 626 | if (player.controller_type == controller_type && player.connected == player_connected) { | ||
| 627 | return; | ||
| 628 | } | ||
| 629 | |||
| 630 | // Disconnect the controller first. | ||
| 631 | UpdateController(controller_type, player_index, false); | ||
| 632 | } | ||
| 633 | |||
| 622 | void ConfigureInputPlayer::showEvent(QShowEvent* event) { | 634 | void ConfigureInputPlayer::showEvent(QShowEvent* event) { |
| 623 | if (bottom_row == nullptr) { | 635 | if (bottom_row == nullptr) { |
| 624 | return; | 636 | 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: | |||
| 54 | /// Save all button configurations to settings file. | 54 | /// Save all button configurations to settings file. |
| 55 | void ApplyConfiguration(); | 55 | void ApplyConfiguration(); |
| 56 | 56 | ||
| 57 | /** | ||
| 58 | * Attempts to connect the currently selected controller in the HID backend. | ||
| 59 | * This function will not do anything if it is not connected in the frontend. | ||
| 60 | */ | ||
| 61 | void TryConnectSelectedController(); | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Attempts to disconnect the currently selected controller in the HID backend. | ||
| 65 | * This function will not do anything if the configuration has not changed. | ||
| 66 | */ | ||
| 67 | void TryDisconnectSelectedController(); | ||
| 68 | |||
| 57 | /// Set the connection state checkbox (used to sync state). | 69 | /// Set the connection state checkbox (used to sync state). |
| 58 | void ConnectPlayer(bool connected); | 70 | void ConnectPlayer(bool connected); |
| 59 | 71 | ||
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; | |||
| 142 | /** | 142 | /** |
| 143 | * "Callouts" are one-time instructional messages shown to the user. In the config settings, there | 143 | * "Callouts" are one-time instructional messages shown to the user. In the config settings, there |
| 144 | * is a bitfield "callout_flags" options, used to track if a message has already been shown to the | 144 | * is a bitfield "callout_flags" options, used to track if a message has already been shown to the |
| 145 | * user. This is 32-bits - if we have more than 32 callouts, we should retire and recyle old ones. | 145 | * user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones. |
| 146 | */ | 146 | */ |
| 147 | enum class CalloutFlag : uint32_t { | 147 | enum class CalloutFlag : uint32_t { |
| 148 | Telemetry = 0x1, | 148 | Telemetry = 0x1, |
| @@ -580,9 +580,8 @@ void GMainWindow::InitializeWidgets() { | |||
| 580 | if (emulation_running) { | 580 | if (emulation_running) { |
| 581 | return; | 581 | return; |
| 582 | } | 582 | } |
| 583 | const bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() || | 583 | Settings::values.use_asynchronous_gpu_emulation.SetValue( |
| 584 | Settings::values.use_multi_core.GetValue(); | 584 | !Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
| 585 | Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); | ||
| 586 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); | 585 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
| 587 | Settings::Apply(Core::System::GetInstance()); | 586 | Settings::Apply(Core::System::GetInstance()); |
| 588 | }); | 587 | }); |
| @@ -599,16 +598,13 @@ void GMainWindow::InitializeWidgets() { | |||
| 599 | return; | 598 | return; |
| 600 | } | 599 | } |
| 601 | Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue()); | 600 | Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue()); |
| 602 | const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() || | ||
| 603 | Settings::values.use_multi_core.GetValue(); | ||
| 604 | Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async); | ||
| 605 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); | ||
| 606 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); | 601 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); |
| 607 | Settings::Apply(Core::System::GetInstance()); | 602 | Settings::Apply(Core::System::GetInstance()); |
| 608 | }); | 603 | }); |
| 609 | multicore_status_button->setText(tr("MULTICORE")); | 604 | multicore_status_button->setText(tr("MULTICORE")); |
| 610 | multicore_status_button->setCheckable(true); | 605 | multicore_status_button->setCheckable(true); |
| 611 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); | 606 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); |
| 607 | |||
| 612 | statusBar()->insertPermanentWidget(0, multicore_status_button); | 608 | statusBar()->insertPermanentWidget(0, multicore_status_button); |
| 613 | statusBar()->insertPermanentWidget(0, async_status_button); | 609 | statusBar()->insertPermanentWidget(0, async_status_button); |
| 614 | 610 | ||
| @@ -1049,20 +1045,23 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { | |||
| 1049 | break; | 1045 | break; |
| 1050 | 1046 | ||
| 1051 | default: | 1047 | default: |
| 1052 | if (static_cast<u32>(result) > | 1048 | if (result > Core::System::ResultStatus::ErrorLoader) { |
| 1053 | static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { | ||
| 1054 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | 1049 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); |
| 1055 | const u16 error_id = static_cast<u16>(result) - loader_id; | 1050 | const u16 error_id = static_cast<u16>(result) - loader_id; |
| 1056 | const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id); | 1051 | const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id); |
| 1057 | LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code); | 1052 | LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code); |
| 1058 | QMessageBox::critical( | 1053 | |
| 1059 | this, | 1054 | const auto title = |
| 1060 | tr("Error while loading ROM! ").append(QString::fromStdString(error_code)), | 1055 | tr("Error while loading ROM! %1", "%1 signifies a numeric error code.") |
| 1061 | QString::fromStdString(fmt::format( | 1056 | .arg(QString::fromStdString(error_code)); |
| 1062 | "{}<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the " | 1057 | const auto description = |
| 1063 | "yuzu quickstart guide</a> to redump your files.<br>You can refer " | 1058 | tr("%1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the " |
| 1064 | "to the yuzu wiki</a> or the yuzu Discord</a> for help.", | 1059 | "yuzu quickstart guide</a> to redump your files.<br>You can refer " |
| 1065 | static_cast<Loader::ResultStatus>(error_id)))); | 1060 | "to the yuzu wiki</a> or the yuzu Discord</a> for help.", |
| 1061 | "%1 signifies a numeric error ID.") | ||
| 1062 | .arg(error_id); | ||
| 1063 | |||
| 1064 | QMessageBox::critical(this, title, description); | ||
| 1066 | } else { | 1065 | } else { |
| 1067 | QMessageBox::critical( | 1066 | QMessageBox::critical( |
| 1068 | this, tr("Error while loading ROM!"), | 1067 | this, tr("Error while loading ROM!"), |
| @@ -2533,9 +2532,6 @@ void GMainWindow::UpdateStatusBar() { | |||
| 2533 | void GMainWindow::UpdateStatusButtons() { | 2532 | void GMainWindow::UpdateStatusButtons() { |
| 2534 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); | 2533 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); |
| 2535 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); | 2534 | multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue()); |
| 2536 | Settings::values.use_asynchronous_gpu_emulation.SetValue( | ||
| 2537 | Settings::values.use_asynchronous_gpu_emulation.GetValue() || | ||
| 2538 | Settings::values.use_multi_core.GetValue()); | ||
| 2539 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); | 2535 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
| 2540 | renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == | 2536 | renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == |
| 2541 | Settings::RendererBackend::Vulkan); | 2537 | 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) { | |||
| 202 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | 202 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); |
| 203 | const u16 error_id = static_cast<u16>(load_result) - loader_id; | 203 | const u16 error_id = static_cast<u16>(load_result) - loader_id; |
| 204 | LOG_CRITICAL(Frontend, | 204 | LOG_CRITICAL(Frontend, |
| 205 | "While attempting to load the ROM requested, an error occured. Please " | 205 | "While attempting to load the ROM requested, an error occurred. Please " |
| 206 | "refer to the yuzu wiki for more information or the yuzu discord for " | 206 | "refer to the yuzu wiki for more information or the yuzu discord for " |
| 207 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | 207 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", |
| 208 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); | 208 | 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) { | |||
| 242 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); | 242 | const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); |
| 243 | const u16 error_id = static_cast<u16>(load_result) - loader_id; | 243 | const u16 error_id = static_cast<u16>(load_result) - loader_id; |
| 244 | LOG_CRITICAL(Frontend, | 244 | LOG_CRITICAL(Frontend, |
| 245 | "While attempting to load the ROM requested, an error occured. Please " | 245 | "While attempting to load the ROM requested, an error occurred. Please " |
| 246 | "refer to the yuzu wiki for more information or the yuzu discord for " | 246 | "refer to the yuzu wiki for more information or the yuzu discord for " |
| 247 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", | 247 | "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", |
| 248 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); | 248 | loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); |