diff --git a/README.md b/README.md index 97b013469..ec13cd3ac 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 4034. +This is the source code for early-access 4035. ## Legal Notice diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 141eb6928..ddd6f6185 100755 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -18,9 +18,7 @@ constexpr auto INCREMENT_TIME{5ms}; DeviceSession::DeviceSession(Core::System& system_) : system{system_}, thread_event{Core::Timing::CreateEvent( "AudioOutSampleTick", - [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { - return ThreadFunc(); - })} {} + [this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {} DeviceSession::~DeviceSession() { Finalize(); diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 6edd6bdbd..f1483331f 100755 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -64,6 +64,8 @@ add_library(common STATIC fs/path_util.cpp fs/path_util.h hash.h + heap_tracker.cpp + heap_tracker.h hex_util.cpp hex_util.h host_memory.cpp diff --git a/src/common/assert.cpp b/src/common/assert.cpp index 431813a4e..78a8e48d7 100755 --- a/src/common/assert.cpp +++ b/src/common/assert.cpp @@ -3,16 +3,19 @@ #include "common/assert.h" #include "common/common_funcs.h" +#include "common/logging/backend.h" #include "common/settings.h" void assert_fail_impl() { if (Settings::values.use_debug_asserts) { + Common::Log::Stop(); Crash(); } } [[noreturn]] void unreachable_impl() { + Common::Log::Stop(); Crash(); throw std::runtime_error("Unreachable code"); } diff --git a/src/common/heap_tracker.cpp b/src/common/heap_tracker.cpp new file mode 100755 index 000000000..95dc8aa1e --- /dev/null +++ b/src/common/heap_tracker.cpp @@ -0,0 +1,263 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/heap_tracker.h" +#include "common/logging/log.h" + +namespace Common { + +namespace { + +constexpr s64 MaxResidentMapCount = 0x8000; + +} // namespace + +HeapTracker::HeapTracker(Common::HostMemory& buffer) : m_buffer(buffer) {} +HeapTracker::~HeapTracker() = default; + +void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length, + MemoryPermission perm, bool is_separate_heap) { + // When mapping other memory, map pages immediately. + if (!is_separate_heap) { + m_buffer.Map(virtual_offset, host_offset, length, perm, false); + return; + } + + { + // We are mapping part of a separate heap. + std::scoped_lock lk{m_lock}; + + auto* const map = new SeparateHeapMap{ + .vaddr = virtual_offset, + .paddr = host_offset, + .size = length, + .tick = m_tick++, + .perm = perm, + .is_resident = false, + }; + + // Insert into mappings. + m_map_count++; + m_mappings.insert(*map); + } + + // Finally, map. + this->DeferredMapSeparateHeap(virtual_offset); +} + +void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) { + // If this is a separate heap... + if (is_separate_heap) { + std::scoped_lock lk{m_lock}; + + const SeparateHeapMap key{ + .vaddr = virtual_offset, + }; + + // Split at the boundaries of the region we are removing. + this->SplitHeapMapLocked(virtual_offset); + this->SplitHeapMapLocked(virtual_offset + size); + + // Erase all mappings in range. + auto it = m_mappings.find(key); + while (it != m_mappings.end() && it->vaddr < virtual_offset + size) { + // Get underlying item. + auto* const item = std::addressof(*it); + + // If resident, erase from resident map. + if (item->is_resident) { + ASSERT(--m_resident_map_count >= 0); + m_resident_mappings.erase(m_resident_mappings.iterator_to(*item)); + } + + // Erase from map. + it = m_mappings.erase(it); + ASSERT(--m_map_count >= 0); + + // Free the item. + delete item; + } + } + + // Unmap pages. + m_buffer.Unmap(virtual_offset, size, false); +} + +void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) { + // Ensure no rebuild occurs while reprotecting. + std::shared_lock lk{m_rebuild_lock}; + + // Split at the boundaries of the region we are reprotecting. + this->SplitHeapMap(virtual_offset, size); + + // Declare tracking variables. + VAddr cur = virtual_offset; + VAddr end = virtual_offset + size; + + while (cur < end) { + VAddr next = cur; + bool should_protect = false; + + { + std::scoped_lock lk2{m_lock}; + + const SeparateHeapMap key{ + .vaddr = next, + }; + + // Try to get the next mapping corresponding to this address. + const auto it = m_mappings.nfind(key); + + if (it == m_mappings.end()) { + // There are no separate heap mappings remaining. + next = end; + should_protect = true; + } else if (it->vaddr == cur) { + // We are in range. + // Update permission bits. + it->perm = perm; + + // Determine next address and whether we should protect. + next = cur + it->size; + should_protect = it->is_resident; + } else /* if (it->vaddr > cur) */ { + // We weren't in range, but there is a block coming up that will be. + next = it->vaddr; + should_protect = true; + } + } + + // Clamp to end. + next = std::min(next, end); + + // Reprotect, if we need to. + if (should_protect) { + m_buffer.Protect(cur, next - cur, perm); + } + + // Advance. + cur = next; + } +} + +bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) { + if (m_buffer.IsInVirtualRange(fault_address)) { + return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer()); + } + + return false; +} + +bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) { + bool rebuild_required = false; + + { + std::scoped_lock lk{m_lock}; + + // Check to ensure this was a non-resident separate heap mapping. + const auto it = this->GetNearestHeapMapLocked(virtual_offset); + if (it == m_mappings.end() || it->is_resident) { + return false; + } + + // Update tick before possible rebuild. + it->tick = m_tick++; + + // Check if we need to rebuild. + if (m_resident_map_count > MaxResidentMapCount) { + rebuild_required = true; + } + + // Map the area. + m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false); + + // This map is now resident. + it->is_resident = true; + m_resident_map_count++; + m_resident_mappings.insert(*it); + } + + if (rebuild_required) { + // A rebuild was required, so perform it now. + this->RebuildSeparateHeapAddressSpace(); + } + + return true; +} + +void HeapTracker::RebuildSeparateHeapAddressSpace() { + std::scoped_lock lk{m_rebuild_lock, m_lock}; + + ASSERT(!m_resident_mappings.empty()); + + // Unmap so we have at least 4 maps available. + const size_t desired_count = std::min(m_resident_map_count, MaxResidentMapCount - 4); + const size_t evict_count = m_resident_map_count - desired_count; + auto it = m_resident_mappings.begin(); + + for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) { + // Unmark and unmap. + it->is_resident = false; + m_buffer.Unmap(it->vaddr, it->size, false); + + // Advance. + ASSERT(--m_resident_map_count >= 0); + it = m_resident_mappings.erase(it); + } +} + +void HeapTracker::SplitHeapMap(VAddr offset, size_t size) { + std::scoped_lock lk{m_lock}; + + this->SplitHeapMapLocked(offset); + this->SplitHeapMapLocked(offset + size); +} + +void HeapTracker::SplitHeapMapLocked(VAddr offset) { + const auto it = this->GetNearestHeapMapLocked(offset); + if (it == m_mappings.end() || it->vaddr == offset) { + // Not contained or no split required. + return; + } + + // Cache the original values. + auto* const left = std::addressof(*it); + const size_t orig_size = left->size; + + // Adjust the left map. + const size_t left_size = offset - left->vaddr; + left->size = left_size; + + // Create the new right map. + auto* const right = new SeparateHeapMap{ + .vaddr = left->vaddr + left_size, + .paddr = left->paddr + left_size, + .size = orig_size - left_size, + .tick = left->tick, + .perm = left->perm, + .is_resident = left->is_resident, + }; + + // Insert the new right map. + m_map_count++; + m_mappings.insert(*right); + + // If resident, also insert into resident map. + if (right->is_resident) { + m_resident_mappings.insert(*right); + m_resident_map_count++; + } +} + +HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) { + const SeparateHeapMap key{ + .vaddr = offset, + }; + + return m_mappings.find(key); +} + +} // namespace Common diff --git a/src/common/heap_tracker.h b/src/common/heap_tracker.h new file mode 100755 index 000000000..cc16041d9 --- /dev/null +++ b/src/common/heap_tracker.h @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "common/host_memory.h" +#include "common/intrusive_red_black_tree.h" + +namespace Common { + +struct SeparateHeapMap { + Common::IntrusiveRedBlackTreeNode addr_node{}; + Common::IntrusiveRedBlackTreeNode tick_node{}; + VAddr vaddr{}; + PAddr paddr{}; + size_t size{}; + size_t tick{}; + MemoryPermission perm{}; + bool is_resident{}; +}; + +struct SeparateHeapMapAddrComparator { + static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) { + if (lhs.vaddr < rhs.vaddr) { + return -1; + } else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) { + return 0; + } else { + return 1; + } + } +}; + +struct SeparateHeapMapTickComparator { + static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) { + if (lhs.tick < rhs.tick) { + return -1; + } else if (lhs.tick > rhs.tick) { + return 1; + } else { + return SeparateHeapMapAddrComparator::Compare(lhs, rhs); + } + } +}; + +class HeapTracker { +public: + explicit HeapTracker(Common::HostMemory& buffer); + ~HeapTracker(); + + void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm, + bool is_separate_heap); + void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap); + void Protect(size_t virtual_offset, size_t length, MemoryPermission perm); + u8* VirtualBasePointer() { + return m_buffer.VirtualBasePointer(); + } + + bool DeferredMapSeparateHeap(u8* fault_address); + bool DeferredMapSeparateHeap(size_t virtual_offset); + +private: + using AddrTreeTraits = + Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>; + using AddrTree = AddrTreeTraits::TreeType; + + using TickTreeTraits = + Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>; + using TickTree = TickTreeTraits::TreeType; + + AddrTree m_mappings{}; + TickTree m_resident_mappings{}; + +private: + void SplitHeapMap(VAddr offset, size_t size); + void SplitHeapMapLocked(VAddr offset); + + AddrTree::iterator GetNearestHeapMapLocked(VAddr offset); + + void RebuildSeparateHeapAddressSpace(); + +private: + Common::HostMemory& m_buffer; + + std::shared_mutex m_rebuild_lock{}; + std::mutex m_lock{}; + s64 m_map_count{}; + s64 m_resident_map_count{}; + size_t m_tick{}; +}; + +} // namespace Common diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 530206fcd..7fbf1aa81 100755 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -679,7 +679,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default; HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, - MemoryPermission perms) { + MemoryPermission perms, bool separate_heap) { ASSERT(virtual_offset % PageAlignment == 0); ASSERT(host_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); @@ -691,7 +691,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); } -void HostMemory::Unmap(size_t virtual_offset, size_t length) { +void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) { ASSERT(virtual_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); ASSERT(virtual_offset + length <= virtual_size); @@ -701,14 +701,16 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) { impl->Unmap(virtual_offset + virtual_base_offset, length); } -void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, - bool execute) { +void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) { ASSERT(virtual_offset % PageAlignment == 0); ASSERT(length % PageAlignment == 0); ASSERT(virtual_offset + length <= virtual_size); if (length == 0 || !virtual_base || !impl) { return; } + const bool read = True(perm & MemoryPermission::Read); + const bool write = True(perm & MemoryPermission::Write); + const bool execute = True(perm & MemoryPermission::Execute); impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); } diff --git a/src/common/host_memory.h b/src/common/host_memory.h index b70de6a55..42350fee9 100755 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -40,11 +40,12 @@ public: HostMemory(HostMemory&& other) noexcept; HostMemory& operator=(HostMemory&& other) noexcept; - void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms); + void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, + bool separate_heap); - void Unmap(size_t virtual_offset, size_t length); + void Unmap(size_t virtual_offset, size_t length, bool separate_heap); - void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false); + void Protect(size_t virtual_offset, size_t length, MemoryPermission perms); void EnableDirectMappedAddress(); @@ -64,6 +65,10 @@ public: return virtual_base; } + bool IsInVirtualRange(void* address) const noexcept { + return address >= virtual_base && address < virtual_base + virtual_size; + } + private: size_t backing_size{}; size_t virtual_size{}; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 158007a7d..1168fbf6b 100755 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -208,6 +208,10 @@ public: instance->StartBackendThread(); } + static void Stop() { + instance->StopBackendThread(); + } + Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; @@ -259,6 +263,15 @@ private: }); } + void StopBackendThread() { + backend_thread.request_stop(); + if (backend_thread.joinable()) { + backend_thread.join(); + } + + ForEachBackend([](Backend& backend) { backend.Flush(); }); + } + Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, const char* function, std::string&& message) const { using std::chrono::duration_cast; @@ -313,6 +326,10 @@ void Start() { Impl::Start(); } +void Stop() { + Impl::Stop(); +} + void DisableLoggingInTests() { initialization_in_progress_suppress_logging = true; } diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 9a8e0521b..920ccf52b 100755 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -14,6 +14,9 @@ void Initialize(); void Start(); +/// Explicitly stops the logger thread and flushes the buffers +void Stop(); + void DisableLoggingInTests(); /** diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 6c8561ee6..50877bc15 100755 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -103,7 +103,7 @@ private: // Having them on the same cache-line would result in false-sharing between them. // TODO: Remove this ifdef whenever clang and GCC support // std::hardware_destructive_interference_size. -#if defined(_MSC_VER) && _MSC_VER >= 1911 +#ifdef __cpp_lib_hardware_interference_size alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0}; alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0}; #else diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fb1a02a9b..48e174a9d 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -978,6 +978,7 @@ endif() if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) target_sources(core PRIVATE + arm/dynarmic/arm_dynarmic.cpp arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic_64.cpp arm/dynarmic/arm_dynarmic_64.h diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp new file mode 100755 index 000000000..e6e9fc45b --- /dev/null +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef __linux__ + +#include "common/signal_chain.h" + +#include "core/arm/dynarmic/arm_dynarmic.h" +#include "core/hle/kernel/k_process.h" +#include "core/memory.h" + +namespace Core { + +namespace { + +thread_local Core::Memory::Memory* g_current_memory{}; +std::once_flag g_registered{}; +struct sigaction g_old_segv {}; + +void HandleSigSegv(int sig, siginfo_t* info, void* ctx) { + if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) { + return; + } + + return g_old_segv.sa_sigaction(sig, info, ctx); +} + +} // namespace + +ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) { + g_current_memory = std::addressof(process->GetMemory()); +} + +ScopedJitExecution::~ScopedJitExecution() { + g_current_memory = nullptr; +} + +void ScopedJitExecution::RegisterHandler() { + std::call_once(g_registered, [] { + struct sigaction sa {}; + sa.sa_sigaction = &HandleSigSegv; + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv)); + }); +} + +} // namespace Core + +#endif diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index eef7c3116..53dd18815 100755 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) { return static_cast(hr); } +#ifdef __linux__ + +class ScopedJitExecution { +public: + explicit ScopedJitExecution(Kernel::KProcess* process); + ~ScopedJitExecution(); + static void RegisterHandler(); +}; + +#else + +class ScopedJitExecution { +public: + explicit ScopedJitExecution(Kernel::KProcess* process) {} + ~ScopedJitExecution() {} + static void RegisterHandler() {} +}; + +#endif + } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 079ec51fe..955e37eff 100755 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -331,11 +331,15 @@ bool ArmDynarmic32::IsInThumbMode() const { } HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) { + ScopedJitExecution sj(thread->GetOwnerProcess()); + m_jit->ClearExclusiveState(); return TranslateHaltReason(m_jit->Run()); } HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) { + ScopedJitExecution sj(thread->GetOwnerProcess()); + m_jit->ClearExclusiveState(); return TranslateHaltReason(m_jit->Step()); } @@ -377,6 +381,7 @@ ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProc m_cp15(std::make_shared(*this)), m_core_index{core_index} { auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl(); m_jit = MakeJit(&page_table_impl); + ScopedJitExecution::RegisterHandler(); } ArmDynarmic32::~ArmDynarmic32() = default; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index ac1db0195..9a06db98a 100755 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -362,11 +362,15 @@ std::shared_ptr ArmDynarmic64::MakeJit(Common::PageTable* pa } HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) { + ScopedJitExecution sj(thread->GetOwnerProcess()); + m_jit->ClearExclusiveState(); return TranslateHaltReason(m_jit->Run()); } HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) { + ScopedJitExecution sj(thread->GetOwnerProcess()); + m_jit->ClearExclusiveState(); return TranslateHaltReason(m_jit->Step()); } @@ -406,6 +410,7 @@ ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProc auto& page_table = process->GetPageTable().GetBasePageTable(); auto& page_table_impl = page_table.GetImpl(); m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth()); + ScopedJitExecution::RegisterHandler(); } ArmDynarmic64::~ArmDynarmic64() = default; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index a4a7b232d..5cd970b4d 100755 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -29,7 +29,6 @@ std::shared_ptr CreateEvent(std::string name, TimedCallback&& callbac struct CoreTiming::Event { s64 time; u64 fifo_order; - std::uintptr_t user_data; std::weak_ptr type; s64 reschedule_time; heap_t::handle_type handle{}; @@ -67,17 +66,15 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { event_fifo_id = 0; shutting_down = false; cpu_ticks = 0; - const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds) - -> std::optional { return std::nullopt; }; - ev_lost = CreateEvent("_lost_event", empty_timed_callback); if (is_multicore) { timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); } } void CoreTiming::ClearPendingEvents() { - std::scoped_lock lock{basic_lock}; + std::scoped_lock lock{advance_lock, basic_lock}; event_queue.clear(); + event.Set(); } void CoreTiming::Pause(bool is_paused) { @@ -119,14 +116,12 @@ bool CoreTiming::HasPendingEvents() const { } void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, - const std::shared_ptr& event_type, - std::uintptr_t user_data, bool absolute_time) { + const std::shared_ptr& event_type, bool absolute_time) { { std::scoped_lock scope{basic_lock}; const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; - auto h{event_queue.emplace( - Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})}; + auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, event_type, 0})}; (*h).handle = h; } @@ -136,13 +131,13 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, std::chrono::nanoseconds resched_time, const std::shared_ptr& event_type, - std::uintptr_t user_data, bool absolute_time) { + bool absolute_time) { { std::scoped_lock scope{basic_lock}; const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; - auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type, - resched_time.count()})}; + auto h{event_queue.emplace( + Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()})}; (*h).handle = h; } @@ -150,14 +145,14 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, } void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, - std::uintptr_t user_data, bool wait) { + UnscheduleEventType type) { { std::scoped_lock lk{basic_lock}; std::vector to_remove; for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) { const Event& e = *itr; - if (e.type.lock().get() == event_type.get() && e.user_data == user_data) { + if (e.type.lock().get() == event_type.get()) { to_remove.push_back(itr->handle); } } @@ -165,10 +160,12 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, for (auto h : to_remove) { event_queue.erase(h); } + + event_type->sequence_number++; } // Force any in-progress events to finish - if (wait) { + if (type == UnscheduleEventType::Wait) { std::scoped_lock lk{advance_lock}; } } @@ -208,28 +205,31 @@ std::optional CoreTiming::Advance() { const Event& evt = event_queue.top(); if (const auto event_type{evt.type.lock()}) { - if (evt.reschedule_time == 0) { - const auto evt_user_data = evt.user_data; - const auto evt_time = evt.time; + const auto evt_time = evt.time; + const auto evt_sequence_num = event_type->sequence_number; + if (evt.reschedule_time == 0) { event_queue.pop(); basic_lock.unlock(); event_type->callback( - evt_user_data, evt_time, - std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time}); + evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time}); basic_lock.lock(); } else { basic_lock.unlock(); const auto new_schedule_time{event_type->callback( - evt.user_data, evt.time, - std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})}; + evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time})}; basic_lock.lock(); + if (evt_sequence_num != event_type->sequence_number) { + // Heap handle is invalidated after external modification. + continue; + } + const auto next_schedule_time{new_schedule_time.has_value() ? new_schedule_time.value().count() : evt.reschedule_time}; @@ -241,8 +241,8 @@ std::optional CoreTiming::Advance() { next_time = pause_end_time + next_schedule_time; } - event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data, - evt.type, next_schedule_time, evt.handle}); + event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type, + next_schedule_time, evt.handle}); } } diff --git a/src/core/core_timing.h b/src/core/core_timing.h index a4e99e386..1e6291196 100755 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -22,17 +22,25 @@ namespace Core::Timing { /// A callback that may be scheduled for a particular core timing event. using TimedCallback = std::function( - std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; + s64 time, std::chrono::nanoseconds ns_late)>; /// Contains the characteristics of a particular event. struct EventType { explicit EventType(TimedCallback&& callback_, std::string&& name_) - : callback{std::move(callback_)}, name{std::move(name_)} {} + : callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {} /// The event's callback function. TimedCallback callback; /// A pointer to the name of the event. const std::string name; + /// A monotonic sequence number, incremented when this event is + /// changed externally. + size_t sequence_number; +}; + +enum class UnscheduleEventType { + Wait, + NoWait, }; /** @@ -89,23 +97,17 @@ public: /// Schedules an event in core timing void ScheduleEvent(std::chrono::nanoseconds ns_into_future, - const std::shared_ptr& event_type, std::uintptr_t user_data = 0, - bool absolute_time = false); + const std::shared_ptr& event_type, bool absolute_time = false); /// Schedules an event which will automatically re-schedule itself with the given time, until /// unscheduled void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, std::chrono::nanoseconds resched_time, const std::shared_ptr& event_type, - std::uintptr_t user_data = 0, bool absolute_time = false); + bool absolute_time = false); - void UnscheduleEvent(const std::shared_ptr& event_type, std::uintptr_t user_data, - bool wait = true); - - void UnscheduleEventWithoutWait(const std::shared_ptr& event_type, - std::uintptr_t user_data) { - UnscheduleEvent(event_type, user_data, false); - } + void UnscheduleEvent(const std::shared_ptr& event_type, + UnscheduleEventType type = UnscheduleEventType::Wait); void AddTicks(u64 ticks_to_add); @@ -158,7 +160,6 @@ private: heap_t event_queue; u64 event_fifo_id = 0; - std::shared_ptr ev_lost; Common::Event event{}; Common::Event pause_event{}; mutable std::mutex basic_lock; diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp index 66ab808b5..82dd90200 100755 --- a/src/core/hle/kernel/k_auto_object_container.cpp +++ b/src/core/hle/kernel/k_auto_object_container.cpp @@ -8,19 +8,22 @@ namespace Kernel { void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) { - KScopedLightLock lk(m_lock); + // KScopedInterruptDisable di; + KScopedSpinLock lk(m_lock); m_object_list.insert_unique(*obj); } void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) { - KScopedLightLock lk(m_lock); + // KScopedInterruptDisable di; + KScopedSpinLock lk(m_lock); m_object_list.erase(*obj); } size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) { - KScopedLightLock lk(m_lock); + // KScopedInterruptDisable di; + KScopedSpinLock lk(m_lock); return std::count_if(m_object_list.begin(), m_object_list.end(), [&](const auto& obj) { return obj.GetOwner() == owner; }); diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h index 282452c7f..3b8976ef8 100755 --- a/src/core/hle/kernel/k_auto_object_container.h +++ b/src/core/hle/kernel/k_auto_object_container.h @@ -7,7 +7,7 @@ #include "common/common_funcs.h" #include "core/hle/kernel/k_auto_object.h" -#include "core/hle/kernel/k_light_lock.h" +#include "core/hle/kernel/k_spin_lock.h" namespace Kernel { @@ -21,32 +21,7 @@ public: using ListType = boost::intrusive::rbtree; - class ListAccessor : public KScopedLightLock { - public: - explicit ListAccessor(KAutoObjectWithListContainer* container) - : KScopedLightLock(container->m_lock), m_list(container->m_object_list) {} - explicit ListAccessor(KAutoObjectWithListContainer& container) - : KScopedLightLock(container.m_lock), m_list(container.m_object_list) {} - - typename ListType::iterator begin() const { - return m_list.begin(); - } - - typename ListType::iterator end() const { - return m_list.end(); - } - - typename ListType::iterator find(typename ListType::const_reference ref) const { - return m_list.find(ref); - } - - private: - ListType& m_list; - }; - - friend class ListAccessor; - - KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {} + KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {} void Initialize() {} void Finalize() {} @@ -56,7 +31,7 @@ public: size_t GetOwnedCount(KProcess* owner); private: - KLightLock m_lock; + KSpinLock m_lock; ListType m_object_list; }; diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index fa8aaea71..8102b8ae3 100755 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h @@ -30,7 +30,7 @@ public: public: explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {} - Result Initialize(KProcess* owner, s32 size) { + Result Initialize(s32 size) { // Check that the table size is valid. R_UNLESS(size <= static_cast(MaxTableSize), ResultOutOfMemory); @@ -44,7 +44,6 @@ public: m_next_linear_id = MinLinearId; m_count = 0; m_free_head_index = -1; - m_owner = owner; // Free all entries. for (s32 i = 0; i < static_cast(m_table_size); ++i) { @@ -91,8 +90,7 @@ public: // Handle pseudo-handles. if constexpr (std::derived_from) { if (handle == Svc::PseudoHandle::CurrentProcess) { - // TODO: this should be the current process - auto* const cur_process = m_owner; + auto* const cur_process = GetCurrentProcessPointer(m_kernel); ASSERT(cur_process != nullptr); return cur_process; } @@ -302,7 +300,6 @@ private: private: KernelCore& m_kernel; - KProcess* m_owner{}; std::array m_entry_infos{}; std::array m_objects{}; mutable KSpinLock m_lock; diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp index 8e2e40307..4e947dd6b 100755 --- a/src/core/hle/kernel/k_hardware_timer.cpp +++ b/src/core/hle/kernel/k_hardware_timer.cpp @@ -10,15 +10,15 @@ namespace Kernel { void KHardwareTimer::Initialize() { // Create the timing callback to register with CoreTiming. - m_event_type = Core::Timing::CreateEvent( - "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) { - reinterpret_cast(timer_handle)->DoTask(); - return std::nullopt; - }); + m_event_type = Core::Timing::CreateEvent("KHardwareTimer::Callback", + [this](s64, std::chrono::nanoseconds) { + this->DoTask(); + return std::nullopt; + }); } void KHardwareTimer::Finalize() { - m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast(this)); + m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type); m_wakeup_time = std::numeric_limits::max(); m_event_type.reset(); } @@ -57,13 +57,12 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) { m_wakeup_time = wakeup_time; m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time}, - m_event_type, reinterpret_cast(this), - true); + m_event_type, true); } void KHardwareTimer::DisableInterrupt() { - m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type, - reinterpret_cast(this)); + m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, + Core::Timing::UnscheduleEventType::NoWait); m_wakeup_time = std::numeric_limits::max(); } diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 423289145..8c1549559 100755 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -434,7 +434,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool void KPageTableBase::Finalize() { auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { if (Settings::IsFastmemEnabled()) { - m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size); + m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); } }; @@ -5243,7 +5243,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) { // Unmap. R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false, unmap_properties, - OperationType::Unmap, true)); + OperationType::UnmapPhysical, true)); } // Check if we're done. @@ -5326,7 +5326,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) { // Map the papges. R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages, cur_pg, map_properties, - OperationType::MapFirstGroup, false)); + OperationType::MapFirstGroupPhysical, false)); } } @@ -5480,7 +5480,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size) // Unmap. R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false, - unmap_properties, OperationType::Unmap, false)); + unmap_properties, OperationType::UnmapPhysical, false)); } // Check if we're done. @@ -5655,7 +5655,10 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a // or free them to the page list, and so it goes unused (along with page properties). switch (operation) { - case OperationType::Unmap: { + case OperationType::Unmap: + case OperationType::UnmapPhysical: { + const bool separate_heap = operation == OperationType::UnmapPhysical; + // Ensure that any pages we track are closed on exit. KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); @@ -5664,7 +5667,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a this->MakePageGroup(pages_to_close, virt_addr, num_pages); // Unmap. - m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize); + m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap); R_SUCCEED(); } @@ -5672,7 +5675,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a ASSERT(virt_addr != 0); ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr, - ConvertToMemoryPermission(properties.perm)); + ConvertToMemoryPermission(properties.perm), false); // Open references to pages, if we should. if (this->IsHeapPhysicalAddress(phys_addr)) { @@ -5711,16 +5714,19 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a switch (operation) { case OperationType::MapGroup: - case OperationType::MapFirstGroup: { + case OperationType::MapFirstGroup: + case OperationType::MapFirstGroupPhysical: { + const bool separate_heap = operation == OperationType::MapFirstGroupPhysical; + // We want to maintain a new reference to every page in the group. - KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup); + KScopedPageGroup spg(page_group, operation == OperationType::MapGroup); for (const auto& node : page_group) { const size_t size{node.GetNumPages() * PageSize}; // Map the pages. m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(), - ConvertToMemoryPermission(properties.perm)); + ConvertToMemoryPermission(properties.perm), separate_heap); virt_addr += size; } diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h index 556d230b3..077cafc96 100755 --- a/src/core/hle/kernel/k_page_table_base.h +++ b/src/core/hle/kernel/k_page_table_base.h @@ -104,6 +104,9 @@ protected: ChangePermissionsAndRefresh = 5, ChangePermissionsAndRefreshAndFlush = 6, Separate = 7, + + MapFirstGroupPhysical = 65000, + UnmapPhysical = 65001, }; static constexpr size_t MaxPhysicalMapAlignment = 1_GiB; diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 7cb170a2f..c377978b3 100755 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -1237,8 +1237,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) { auto& buffer = m_kernel.System().DeviceMemory().buffer; const auto& code = code_set.CodeSegment(); const auto& patch = code_set.PatchSegment(); - buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true); - buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true); + buffer.Protect(GetInteger(base_addr + code.addr), code.size, + Common::MemoryPermission::Read | Common::MemoryPermission::Execute); + buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, + Common::MemoryPermission::Read | Common::MemoryPermission::Execute); ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None); } #endif diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index c953011d4..0dc8a53f6 100755 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -552,7 +552,7 @@ private: Result InitializeHandleTable(s32 size) { // Try to initialize the handle table. - R_TRY(m_handle_table.Initialize(this, size)); + R_TRY(m_handle_table.Initialize(size)); // We succeeded, so note that we did. m_is_handle_table_initialized = true; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index f6ab2b025..ba41cb573 100755 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -1147,8 +1147,7 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server *out_context = std::make_shared(m_kernel, memory, this, client_thread); (*out_context)->SetSessionRequestManager(manager); - (*out_context) - ->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf); + (*out_context)->PopulateFromIncomingCommandBuffer(cmd_buf); // We succeeded. R_SUCCEED(); } else { diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h index 332a57cb3..81ae2e6cf 100755 --- a/src/core/hle/kernel/k_transfer_memory.h +++ b/src/core/hle/kernel/k_transfer_memory.h @@ -5,6 +5,7 @@ #include +#include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/svc_types.h" diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index fd905276d..2801baa3e 100755 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -238,7 +238,7 @@ struct KernelCore::Impl { void InitializePreemption(KernelCore& kernel) { preemption_event = Core::Timing::CreateEvent( "PreemptionCallback", - [this, &kernel](std::uintptr_t, s64 time, + [this, &kernel](s64 time, std::chrono::nanoseconds) -> std::optional { { KScopedSchedulerLock lock(kernel); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 55391b515..20c6a50a3 100755 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1513,8 +1513,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) return; } - auto transfer_mem = - system.ApplicationProcess()->GetHandleTable().GetObject(handle); + auto transfer_mem = ctx.GetObjectFromHandle(handle); if (transfer_mem.IsNull()) { LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); @@ -1524,8 +1523,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) } std::vector memory(transfer_mem->GetSize()); - system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), - memory.size()); + ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); @@ -1547,8 +1545,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) { return; } - auto transfer_mem = - system.ApplicationProcess()->GetHandleTable().GetObject(handle); + auto transfer_mem = ctx.GetObjectFromHandle(handle); if (transfer_mem.IsNull()) { LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); @@ -1558,8 +1555,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) { } std::vector memory(transfer_mem->GetSize()); - system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), - memory.size()); + ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index ac212d069..c8cc3bb44 100755 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -454,10 +454,8 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) { return; } - const auto& handle_table{system.ApplicationProcess()->GetHandleTable()}; - auto process{handle_table.GetObject(process_handle)}; - auto transfer_memory{ - process->GetHandleTable().GetObject(transfer_memory_handle)}; + auto process{ctx.GetObjectFromHandle(process_handle)}; + auto transfer_memory{ctx.GetObjectFromHandle(transfer_memory_handle)}; const auto session_id{impl->GetSessionId()}; if (session_id == -1) { diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index b9a3be22f..29b3eb511 100755 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -278,9 +278,7 @@ void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { auto params = rp.PopRaw(); auto transfer_memory_size{rp.Pop()}; auto transfer_memory_handle{ctx.GetCopyHandle(0)}; - auto transfer_memory{ - system.ApplicationProcess()->GetHandleTable().GetObject( - transfer_memory_handle)}; + auto transfer_memory{ctx.GetObjectFromHandle(transfer_memory_handle)}; LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", params.sample_rate, params.channel_count, transfer_memory_size); @@ -323,9 +321,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) { auto transfer_memory_size{rp.Pop()}; auto transfer_memory_handle{ctx.GetCopyHandle(0)}; - auto transfer_memory{ - system.ApplicationProcess()->GetHandleTable().GetObject( - transfer_memory_handle)}; + auto transfer_memory{ctx.GetObjectFromHandle(transfer_memory_handle)}; LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " @@ -374,9 +370,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { auto params = rp.PopRaw(); auto transfer_memory_size{rp.Pop()}; auto transfer_memory_handle{ctx.GetCopyHandle(0)}; - auto transfer_memory{ - system.ApplicationProcess()->GetHandleTable().GetObject( - transfer_memory_handle)}; + auto transfer_memory{ctx.GetObjectFromHandle(transfer_memory_handle)}; LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", params.sample_rate, params.channel_count, transfer_memory_size); @@ -414,9 +408,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { auto transfer_memory_size{rp.Pop()}; auto transfer_memory_handle{ctx.GetCopyHandle(0)}; - auto transfer_memory{ - system.ApplicationProcess()->GetHandleTable().GetObject( - transfer_memory_handle)}; + auto transfer_memory{ctx.GetObjectFromHandle(transfer_memory_handle)}; LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 06a01c02c..3174672af 100755 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -1850,8 +1850,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); - auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_1_handle); + auto t_mem_1 = ctx.GetObjectFromHandle(t_mem_1_handle); if (t_mem_1.IsNull()) { LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); @@ -1860,8 +1859,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { return; } - auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_2_handle); + auto t_mem_2 = ctx.GetObjectFromHandle(t_mem_2_handle); if (t_mem_2.IsNull()) { LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); @@ -2142,8 +2140,7 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) { ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); - auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_handle); + auto t_mem = ctx.GetObjectFromHandle(t_mem_handle); if (t_mem.IsNull()) { LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index 9442d8d0c..c49ffcf20 100755 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp @@ -49,10 +49,10 @@ HidBus::HidBus(Core::System& system_) // Register update callbacks hidbus_update_event = Core::Timing::CreateEvent( "Hidbus::UpdateCallback", - [this](std::uintptr_t user_data, s64 time, + [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional { const auto guard = LockService(); - UpdateHidbus(user_data, ns_late); + UpdateHidbus(ns_late); return std::nullopt; }); @@ -61,10 +61,10 @@ HidBus::HidBus(Core::System& system_) } HidBus::~HidBus() { - system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0); + system.CoreTiming().UnscheduleEvent(hidbus_update_event); } -void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) { if (is_hidbus_enabled) { for (std::size_t i = 0; i < devices.size(); ++i) { if (!devices[i].is_device_initializated) { @@ -448,8 +448,7 @@ void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) { ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); - auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_handle); + auto t_mem = ctx.GetObjectFromHandle(t_mem_handle); if (t_mem.IsNull()) { LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h index 6c3857a89..880c98fb5 100755 --- a/src/core/hle/service/hid/hidbus.h +++ b/src/core/hle/service/hid/hidbus.h @@ -108,7 +108,7 @@ private: void DisableJoyPollingReceiveMode(HLERequestContext& ctx); void SetStatusManagerType(HLERequestContext& ctx); - void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateHidbus(std::chrono::nanoseconds ns_late); std::optional GetDeviceIndexFromHandle(BusHandle handle) const; template diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 01de86523..037605e3f 100755 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -197,8 +197,7 @@ void IRS::RunImageTransferProcessor(HLERequestContext& ctx) { const auto parameters{rp.PopRaw()}; const auto t_mem_handle{ctx.GetCopyHandle(0)}; - auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_handle); + auto t_mem = ctx.GetObjectFromHandle(t_mem_handle); if (t_mem.IsNull()) { LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); @@ -444,8 +443,7 @@ void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) { const auto parameters{rp.PopRaw()}; const auto t_mem_handle{ctx.GetCopyHandle(0)}; - auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_handle); + auto t_mem = ctx.GetObjectFromHandle(t_mem_handle); LOG_INFO(Service_IRS, "called, npad_type={}, npad_id={}, transfer_memory_size={}, " diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp index 6c6cbd802..afc61f70d 100755 --- a/src/core/hle/service/hid/resource_manager.cpp +++ b/src/core/hle/service/hid/resource_manager.cpp @@ -227,8 +227,7 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) { applet_resource->EnableTouchScreen(aruid, is_enabled); } -void ResourceManager::UpdateControllers(std::uintptr_t user_data, - std::chrono::nanoseconds ns_late) { +void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); debug_pad->OnUpdate(core_timing); digitizer->OnUpdate(core_timing); @@ -241,20 +240,19 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data, capture_button->OnUpdate(core_timing); } -void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +void ResourceManager::UpdateNpad(std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); npad->OnUpdate(core_timing); } -void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, - std::chrono::nanoseconds ns_late) { +void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); mouse->OnUpdate(core_timing); debug_mouse->OnUpdate(core_timing); keyboard->OnUpdate(core_timing); } -void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { +void ResourceManager::UpdateMotion(std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); six_axis->OnUpdate(core_timing); seven_six_axis->OnUpdate(core_timing); @@ -273,34 +271,34 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr std::optional { + [this, resource]( + s64 time, std::chrono::nanoseconds ns_late) -> std::optional { const auto guard = LockService(); - resource->UpdateNpad(user_data, ns_late); + resource->UpdateNpad(ns_late); return std::nullopt; }); default_update_event = Core::Timing::CreateEvent( "HID::UpdateDefaultCallback", - [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) - -> std::optional { + [this, resource]( + s64 time, std::chrono::nanoseconds ns_late) -> std::optional { const auto guard = LockService(); - resource->UpdateControllers(user_data, ns_late); + resource->UpdateControllers(ns_late); return std::nullopt; }); mouse_keyboard_update_event = Core::Timing::CreateEvent( "HID::UpdateMouseKeyboardCallback", - [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) - -> std::optional { + [this, resource]( + s64 time, std::chrono::nanoseconds ns_late) -> std::optional { const auto guard = LockService(); - resource->UpdateMouseKeyboard(user_data, ns_late); + resource->UpdateMouseKeyboard(ns_late); return std::nullopt; }); motion_update_event = Core::Timing::CreateEvent( "HID::UpdateMotionCallback", - [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) - -> std::optional { + [this, resource]( + s64 time, std::chrono::nanoseconds ns_late) -> std::optional { const auto guard = LockService(); - resource->UpdateMotion(user_data, ns_late); + resource->UpdateMotion(ns_late); return std::nullopt; }); @@ -314,10 +312,10 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptrFreeAppletResourceId(aruid); } diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h index 5ad7cb564..5a6596099 100755 --- a/src/core/hle/service/hid/resource_manager.h +++ b/src/core/hle/service/hid/resource_manager.h @@ -81,10 +81,10 @@ public: void EnablePadInput(u64 aruid, bool is_enabled); void EnableTouchScreen(u64 aruid, bool is_enabled); - void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateControllers(std::chrono::nanoseconds ns_late); + void UpdateNpad(std::chrono::nanoseconds ns_late); + void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late); + void UpdateMotion(std::chrono::nanoseconds ns_late); private: Result CreateAppletResourceImpl(u64 aruid); diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index 38955932c..39df77e43 100755 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -146,10 +146,7 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory:: HLERequestContext::~HLERequestContext() = default; -void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, - bool incoming) { - client_handle_table = &process.GetHandleTable(); - +void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { IPC::RequestParser rp(src_cmdbuf); command_header = rp.PopRaw(); @@ -162,7 +159,7 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr if (command_header->enable_handle_descriptor) { handle_descriptor_header = rp.PopRaw(); if (handle_descriptor_header->send_current_pid) { - pid = process.GetProcessId(); + pid = thread->GetOwnerProcess()->GetProcessId(); rp.Skip(2, false); } if (incoming) { @@ -270,9 +267,10 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. } -Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, - u32_le* src_cmdbuf) { - ParseCommandBuffer(process, src_cmdbuf, true); +Result HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf) { + client_handle_table = &thread->GetOwnerProcess()->GetHandleTable(); + + ParseCommandBuffer(src_cmdbuf, true); if (command_header->IsCloseCommand()) { // Close does not populate the rest of the IPC header @@ -284,9 +282,9 @@ Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& pr return ResultSuccess; } -Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread) { +Result HLERequestContext::WriteToOutgoingCommandBuffer() { auto current_offset = handles_offset; - auto& owner_process = *requesting_thread.GetOwnerProcess(); + auto& owner_process = *thread->GetOwnerProcess(); auto& handle_table = owner_process.GetHandleTable(); for (auto& object : outgoing_copy_objects) { @@ -319,7 +317,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesti } // Copy the translated command buffer back into the thread's command buffer area. - memory.WriteBlock(requesting_thread.GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32)); + memory.WriteBlock(thread->GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32)); return ResultSuccess; } diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index 18d464c63..40d86943e 100755 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -17,6 +17,7 @@ #include "common/concepts.h" #include "common/swap.h" #include "core/hle/ipc.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/svc_common.h" union Result; @@ -196,10 +197,10 @@ public: } /// Populates this context with data from the requesting process/thread. - Result PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf); + Result PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf); /// Writes data from this context back to the requesting process/thread. - Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread); + Result WriteToOutgoingCommandBuffer(); [[nodiscard]] u32_le GetHipcCommand() const { return command; @@ -359,8 +360,17 @@ public: return *thread; } - Kernel::KHandleTable& GetClientHandleTable() { - return *client_handle_table; + [[nodiscard]] Core::Memory::Memory& GetMemory() const { + return memory; + } + + template + Kernel::KScopedAutoObject GetObjectFromHandle(u32 handle) { + auto obj = client_handle_table->GetObjectForIpc(handle, thread); + if (obj.IsNotNull()) { + return obj->DynamicCast(); + } + return nullptr; } [[nodiscard]] std::shared_ptr GetManager() const { @@ -378,7 +388,7 @@ public: private: friend class IPC::ResponseBuilder; - void ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, bool incoming); + void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming); std::array cmd_buf; Kernel::KServerSession* server_session{}; diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 3a8108252..5dc9a96c0 100755 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -26,7 +26,7 @@ public: explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx, CodeRange user_ro) : ServiceFramework{system_, "IJitEnvironment"}, process{&process_}, - context{system_.ApplicationMemory()} { + context{process->GetMemory()} { // clang-format off static const FunctionInfo functions[] = { {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, @@ -188,7 +188,7 @@ public: return; } - auto tmem{process->GetHandleTable().GetObject(tmem_handle)}; + auto tmem{ctx.GetObjectFromHandle(tmem_handle)}; if (tmem.IsNull()) { LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); IPC::ResponseBuilder rb{ctx, 2}; @@ -356,11 +356,7 @@ public: return; } - // Fetch using the handle table for the application process here, - // since we are not multiprocess yet. - const auto& handle_table{system.ApplicationProcess()->GetHandleTable()}; - - auto process{handle_table.GetObject(process_handle)}; + auto process{ctx.GetObjectFromHandle(process_handle)}; if (process.IsNull()) { LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); IPC::ResponseBuilder rb{ctx, 2}; @@ -368,7 +364,7 @@ public: return; } - auto rx_mem{handle_table.GetObject(rx_mem_handle)}; + auto rx_mem{ctx.GetObjectFromHandle(rx_mem_handle)}; if (rx_mem.IsNull()) { LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); IPC::ResponseBuilder rb{ctx, 2}; @@ -376,7 +372,7 @@ public: return; } - auto ro_mem{handle_table.GetObject(ro_mem_handle)}; + auto ro_mem{ctx.GetObjectFromHandle(ro_mem_handle)}; if (ro_mem.IsNull()) { LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 6352b09a9..aa8aaa2d9 100755 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -67,7 +67,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_ // Schedule the screen composition events multi_composition_event = Core::Timing::CreateEvent( "ScreenComposition", - [this](std::uintptr_t, s64 time, + [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional { vsync_signal.Set(); return std::chrono::nanoseconds(GetNextTicks()); @@ -75,7 +75,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_ single_composition_event = Core::Timing::CreateEvent( "ScreenComposition", - [this](std::uintptr_t, s64 time, + [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional { const auto lock_guard = Lock(); Compose(); @@ -93,11 +93,11 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_ Nvnflinger::~Nvnflinger() { if (system.IsMulticore()) { - system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); + system.CoreTiming().UnscheduleEvent(multi_composition_event); vsync_thread.request_stop(); vsync_signal.Set(); } else { - system.CoreTiming().UnscheduleEvent(single_composition_event, {}); + system.CoreTiming().UnscheduleEvent(single_composition_event); } ShutdownLayers(); diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index 17110d3f1..f0658bb5d 100755 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -651,10 +651,9 @@ private: void RegisterProcessHandle(HLERequestContext& ctx) { LOG_DEBUG(Service_LDR, "(called)"); - auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0)); + auto process = ctx.GetObjectFromHandle(ctx.GetCopyHandle(0)); auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessHandle(client_pid, - process_h->DynamicCast()); + auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); @@ -671,12 +670,11 @@ private: IPC::RequestParser rp{ctx}; auto params = rp.PopRaw(); - auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0)); + auto process = ctx.GetObjectFromHandle(ctx.GetCopyHandle(0)); auto client_pid = ctx.GetPID(); - auto result = - interface.RegisterProcessModuleInfo(client_pid, params.nrr_address, params.nrr_size, - process_h->DynamicCast()); + auto result = interface.RegisterProcessModuleInfo( + client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a0c07de5a..e2060f5f7 100755 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -203,7 +203,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, // If emulation was shutdown, we are closing service threads, do not write the response back to // memory that may be shutting down as well. if (system.IsPoweredOn()) { - ctx.WriteToOutgoingCommandBuffer(ctx.GetThread()); + ctx.WriteToOutgoingCommandBuffer(); } return result; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 8545517b3..b18f2d09d 100755 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/atomic_ops.h" #include "common/common_types.h" +#include "common/heap_tracker.h" #include "common/logging/log.h" #include "common/page_table.h" #include "common/scope_exit.h" @@ -52,10 +53,18 @@ struct Memory::Impl { } else { current_page_table->fastmem_arena = nullptr; } + +#ifdef __linux__ + heap_tracker.emplace(system.DeviceMemory().buffer); + buffer = std::addressof(*heap_tracker); +#else + buffer = std::addressof(system.DeviceMemory().buffer); +#endif } void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, - Common::PhysicalAddress target, Common::MemoryPermission perms) { + Common::PhysicalAddress target, Common::MemoryPermission perms, + bool separate_heap) { ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", @@ -64,19 +73,20 @@ struct Memory::Impl { Common::PageType::Memory); if (current_page_table->fastmem_arena) { - system.DeviceMemory().buffer.Map(GetInteger(base), - GetInteger(target) - DramMemoryMap::Base, size, perms); + buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms, + separate_heap); } } - void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { + void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, + bool separate_heap) { ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0, Common::PageType::Unmapped); if (current_page_table->fastmem_arena) { - system.DeviceMemory().buffer.Unmap(GetInteger(base), size); + buffer->Unmap(GetInteger(base), size, separate_heap); } } @@ -89,11 +99,6 @@ struct Memory::Impl { return; } - const bool is_r = True(perms & Common::MemoryPermission::Read); - const bool is_w = True(perms & Common::MemoryPermission::Write); - const bool is_x = - True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled(); - u64 protect_bytes{}; u64 protect_begin{}; for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) { @@ -102,8 +107,7 @@ struct Memory::Impl { switch (page_type) { case Common::PageType::RasterizerCachedMemory: if (protect_bytes > 0) { - system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, - is_x); + buffer->Protect(protect_begin, protect_bytes, perms); protect_bytes = 0; } break; @@ -116,7 +120,7 @@ struct Memory::Impl { } if (protect_bytes > 0) { - system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, is_x); + buffer->Protect(protect_begin, protect_bytes, perms); } } @@ -486,7 +490,9 @@ struct Memory::Impl { } if (current_page_table->fastmem_arena) { - system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug); + const auto perm{debug ? Common::MemoryPermission{} + : Common::MemoryPermission::ReadWrite}; + buffer->Protect(vaddr, size, perm); } // Iterate over a contiguous CPU address space, marking/unmarking the region. @@ -543,9 +549,14 @@ struct Memory::Impl { } if (current_page_table->fastmem_arena) { - const bool is_read_enable = - !Settings::values.use_reactive_flushing.GetValue() || !cached; - system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); + Common::MemoryPermission perm{}; + if (!Settings::values.use_reactive_flushing.GetValue() || !cached) { + perm |= Common::MemoryPermission::Read; + } + if (!cached) { + perm |= Common::MemoryPermission::Write; + } + buffer->Protect(vaddr, size, perm); } // Iterate over a contiguous CPU address space, which corresponds to the specified GPU @@ -856,6 +867,13 @@ struct Memory::Impl { std::array rasterizer_write_areas{}; std::span gpu_dirty_managers; std::mutex sys_core_guard; + + std::optional heap_tracker; +#ifdef __linux__ + Common::HeapTracker* buffer{}; +#else + Common::HostMemory* buffer{}; +#endif }; Memory::Memory(Core::System& system_) : system{system_} { @@ -873,12 +891,14 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process) { } void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, - Common::PhysicalAddress target, Common::MemoryPermission perms) { - impl->MapMemoryRegion(page_table, base, size, target, perms); + Common::PhysicalAddress target, Common::MemoryPermission perms, + bool separate_heap) { + impl->MapMemoryRegion(page_table, base, size, target, perms, separate_heap); } -void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { - impl->UnmapRegion(page_table, base, size); +void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, + bool separate_heap) { + impl->UnmapRegion(page_table, base, size, separate_heap); } void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size, @@ -1048,7 +1068,9 @@ void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) { } bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { - bool mapped = true; + [[maybe_unused]] bool mapped = true; + [[maybe_unused]] bool rasterizer = false; + u8* const ptr = impl->GetPointerImpl( GetInteger(vaddr), [&] { @@ -1056,8 +1078,26 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { GetInteger(vaddr)); mapped = false; }, - [&] { impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); }); + [&] { + impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); + rasterizer = true; + }); + +#ifdef __linux__ + if (!rasterizer && mapped) { + impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr)); + } +#endif + return mapped && ptr != nullptr; } +bool Memory::InvalidateSeparateHeap(void* fault_address) { +#ifdef __linux__ + return impl->buffer->DeferredMapSeparateHeap(static_cast(fault_address)); +#else + return false; +#endif +} + } // namespace Core::Memory diff --git a/src/core/memory.h b/src/core/memory.h index b14d3dab3..14e5a6126 100755 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -86,7 +86,8 @@ public: * @param perms The permissions to map the memory with. */ void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, - Common::PhysicalAddress target, Common::MemoryPermission perms); + Common::PhysicalAddress target, Common::MemoryPermission perms, + bool separate_heap); /** * Unmaps a region of the emulated process address space. @@ -95,7 +96,8 @@ public: * @param base The address to begin unmapping at. * @param size The amount of bytes to unmap. */ - void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size); + void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, + bool separate_heap); /** * Protects a region of the emulated process address space with the new permissions. @@ -486,6 +488,7 @@ public: void SetGPUDirtyManagers(std::span managers); void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); + bool InvalidateSeparateHeap(void* fault_address); void FlushRegion(Common::ProcessAddress dest_addr, size_t size); private: diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index e5b041744..c9ea35428 100755 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -190,15 +190,15 @@ CheatEngine::CheatEngine(System& system_, std::vector cheats_, } CheatEngine::~CheatEngine() { - core_timing.UnscheduleEvent(event, 0); + core_timing.UnscheduleEvent(event); } void CheatEngine::Initialize() { event = Core::Timing::CreateEvent( "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), - [this](std::uintptr_t user_data, s64 time, + [this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional { - FrameCallback(user_data, ns_late); + FrameCallback(ns_late); return std::nullopt; }); core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event); @@ -239,7 +239,7 @@ void CheatEngine::Reload(std::vector reload_cheats) { MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); -void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { +void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) { if (is_pending_reload.exchange(false)) { vm.LoadProgram(cheats); } diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index ea50f9f86..d4ba1eb5e 100755 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -70,7 +70,7 @@ public: void Reload(std::vector reload_cheats); private: - void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void FrameCallback(std::chrono::nanoseconds ns_late); DmntCheatVm vm; CheatProcessMetadata metadata; diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 6cdbc3bca..1a56d8709 100755 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -51,18 +51,17 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) : core_timing{core_timing_}, memory{memory_} { - event = Core::Timing::CreateEvent( - "MemoryFreezer::FrameCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional { - FrameCallback(user_data, ns_late); - return std::nullopt; - }); + event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback", + [this](s64 time, std::chrono::nanoseconds ns_late) + -> std::optional { + FrameCallback(ns_late); + return std::nullopt; + }); core_timing.ScheduleEvent(memory_freezer_ns, event); } Freezer::~Freezer() { - core_timing.UnscheduleEvent(event, 0); + core_timing.UnscheduleEvent(event); } void Freezer::SetActive(bool is_active) { @@ -159,7 +158,7 @@ Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const { [address](const Entry& entry) { return entry.address == address; }); } -void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { +void Freezer::FrameCallback(std::chrono::nanoseconds ns_late) { if (!IsActive()) { LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); return; diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index 96efb8309..09a2f7f72 100755 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h @@ -77,7 +77,7 @@ private: Entries::iterator FindEntry(VAddr address); Entries::const_iterator FindEntry(VAddr address) const; - void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void FrameCallback(std::chrono::nanoseconds ns_late); void FillEntryReads(); std::atomic_bool active{false}; diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp index e5fe8ad1a..d3acaaaa1 100755 --- a/src/tests/common/host_memory.cpp +++ b/src/tests/common/host_memory.cpp @@ -12,6 +12,7 @@ using namespace Common::Literals; static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; static constexpr size_t BACKING_SIZE = 4_GiB; static constexpr auto PERMS = Common::MemoryPermission::ReadWrite; +static constexpr auto HEAP = false; TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } @@ -20,7 +21,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { TEST_CASE("HostMemory: Simple map", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x5000, 0x8000, 0x1000, PERMS); + mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP); volatile u8* const data = mem.VirtualBasePointer() + 0x5000; data[0] = 50; @@ -29,8 +30,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") { TEST_CASE("HostMemory: Simple mirror map", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x5000, 0x3000, 0x2000, PERMS); - mem.Map(0x8000, 0x4000, 0x1000, PERMS); + mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); + mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP); volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000; volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000; @@ -40,116 +41,116 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") { TEST_CASE("HostMemory: Simple unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x5000, 0x3000, 0x2000, PERMS); + mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); volatile u8* const data = mem.VirtualBasePointer() + 0x5000; data[75] = 50; REQUIRE(data[75] == 50); - mem.Unmap(0x5000, 0x2000); + mem.Unmap(0x5000, 0x2000, HEAP); } TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x5000, 0x3000, 0x2000, PERMS); + mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); volatile u8* const data = mem.VirtualBasePointer() + 0x5000; data[0] = 50; REQUIRE(data[0] == 50); - mem.Unmap(0x5000, 0x2000); + mem.Unmap(0x5000, 0x2000, HEAP); - mem.Map(0x5000, 0x3000, 0x2000, PERMS); + mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); REQUIRE(data[0] == 50); - mem.Map(0x7000, 0x2000, 0x5000, PERMS); + mem.Map(0x7000, 0x2000, 0x5000, PERMS, HEAP); REQUIRE(data[0x3000] == 50); } TEST_CASE("HostMemory: Nieche allocation", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x0000, 0, 0x20000, PERMS); - mem.Unmap(0x0000, 0x4000); - mem.Map(0x1000, 0, 0x2000, PERMS); - mem.Map(0x3000, 0, 0x1000, PERMS); - mem.Map(0, 0, 0x1000, PERMS); + mem.Map(0x0000, 0, 0x20000, PERMS, HEAP); + mem.Unmap(0x0000, 0x4000, HEAP); + mem.Map(0x1000, 0, 0x2000, PERMS, HEAP); + mem.Map(0x3000, 0, 0x1000, PERMS, HEAP); + mem.Map(0, 0, 0x1000, PERMS, HEAP); } TEST_CASE("HostMemory: Full unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x8000, 0, 0x4000, PERMS); - mem.Unmap(0x8000, 0x4000); - mem.Map(0x6000, 0, 0x16000, PERMS); + mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); + mem.Unmap(0x8000, 0x4000, HEAP); + mem.Map(0x6000, 0, 0x16000, PERMS, HEAP); } TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x0000, 0, 0x4000, PERMS); - mem.Unmap(0x2000, 0x4000); - mem.Map(0x2000, 0x80000, 0x4000, PERMS); + mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); + mem.Unmap(0x2000, 0x4000, HEAP); + mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP); } TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x8000, 0, 0x4000, PERMS); - mem.Unmap(0x6000, 0x4000); - mem.Map(0x8000, 0, 0x2000, PERMS); + mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); + mem.Unmap(0x6000, 0x4000, HEAP); + mem.Map(0x8000, 0, 0x2000, PERMS, HEAP); } TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x0000, 0, 0x4000, PERMS); - mem.Map(0x4000, 0, 0x1b000, PERMS); - mem.Unmap(0x3000, 0x1c000); - mem.Map(0x3000, 0, 0x20000, PERMS); + mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); + mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP); + mem.Unmap(0x3000, 0x1c000, HEAP); + mem.Map(0x3000, 0, 0x20000, PERMS, HEAP); } TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x0000, 0, 0x4000, PERMS); - mem.Map(0x4000, 0, 0x4000, PERMS); - mem.Unmap(0x2000, 0x4000); - mem.Map(0x2000, 0, 0x4000, PERMS); + mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); + mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); + mem.Unmap(0x2000, 0x4000, HEAP); + mem.Map(0x2000, 0, 0x4000, PERMS, HEAP); } TEST_CASE("HostMemory: Unmap to origin", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x4000, 0, 0x4000, PERMS); - mem.Map(0x8000, 0, 0x4000, PERMS); - mem.Unmap(0x4000, 0x4000); - mem.Map(0, 0, 0x4000, PERMS); - mem.Map(0x4000, 0, 0x4000, PERMS); + mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); + mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); + mem.Unmap(0x4000, 0x4000, HEAP); + mem.Map(0, 0, 0x4000, PERMS, HEAP); + mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); } TEST_CASE("HostMemory: Unmap to right", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x4000, 0, 0x4000, PERMS); - mem.Map(0x8000, 0, 0x4000, PERMS); - mem.Unmap(0x8000, 0x4000); - mem.Map(0x8000, 0, 0x4000, PERMS); + mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); + mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); + mem.Unmap(0x8000, 0x4000, HEAP); + mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); } TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x4000, 0x10000, 0x4000, PERMS); + mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; ptr[0x1000] = 17; - mem.Unmap(0x6000, 0x2000); + mem.Unmap(0x6000, 0x2000, HEAP); REQUIRE(ptr[0x1000] == 17); } TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x4000, 0x10000, 0x4000, PERMS); + mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; ptr[0x3000] = 19; ptr[0x3fff] = 12; - mem.Unmap(0x4000, 0x2000); + mem.Unmap(0x4000, 0x2000, HEAP); REQUIRE(ptr[0x3000] == 19); REQUIRE(ptr[0x3fff] == 12); @@ -157,13 +158,13 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x4000, 0x10000, 0x4000, PERMS); + mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; ptr[0x0000] = 19; ptr[0x3fff] = 12; - mem.Unmap(0x1000, 0x2000); + mem.Unmap(0x1000, 0x2000, HEAP); REQUIRE(ptr[0x0000] == 19); REQUIRE(ptr[0x3fff] == 12); @@ -171,14 +172,14 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); - mem.Map(0x4000, 0x10000, 0x2000, PERMS); - mem.Map(0x6000, 0x20000, 0x2000, PERMS); + mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP); + mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP); volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; ptr[0x0000] = 19; ptr[0x3fff] = 12; - mem.Unmap(0x5000, 0x2000); + mem.Unmap(0x5000, 0x2000, HEAP); REQUIRE(ptr[0x0000] == 19); REQUIRE(ptr[0x3fff] == 12); diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 5c238520e..976b33598 100755 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -16,20 +16,16 @@ namespace { // Numbers are chosen randomly to make sure the correct one is given. -constexpr std::array CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; constexpr std::array calls_order{{2, 0, 1, 4, 3}}; std::array delays{}; - -std::bitset callbacks_ran_flags; +std::bitset<5> callbacks_ran_flags; u64 expected_callback = 0; template -std::optional HostCallbackTemplate(std::uintptr_t user_data, s64 time, +std::optional HostCallbackTemplate(s64 time, std::chrono::nanoseconds ns_late) { - static_assert(IDX < CB_IDS.size(), "IDX out of range"); + static_assert(IDX < callbacks_ran_flags.size(), "IDX out of range"); callbacks_ran_flags.set(IDX); - REQUIRE(CB_IDS[IDX] == user_data); - REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); delays[IDX] = ns_late.count(); ++expected_callback; return std::nullopt; @@ -76,7 +72,7 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") { const u64 order = calls_order[i]; const auto future_ns = std::chrono::nanoseconds{static_cast(i * one_micro + 100)}; - core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]); + core_timing.ScheduleEvent(future_ns, events[order]); } /// test pause REQUIRE(callbacks_ran_flags.none()); @@ -118,7 +114,7 @@ TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") { for (std::size_t i = 0; i < events.size(); i++) { const u64 order = calls_order[i]; const auto future_ns = std::chrono::nanoseconds{static_cast(i * one_micro + 100)}; - core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]); + core_timing.ScheduleEvent(future_ns, events[order]); } const u64 end = core_timing.GetGlobalTimeNs().count(); diff --git a/src/video_core/texture_cache/decode_bc.cpp b/src/video_core/texture_cache/decode_bc.cpp index 3e26474a3..a018c6df4 100755 --- a/src/video_core/texture_cache/decode_bc.cpp +++ b/src/video_core/texture_cache/decode_bc.cpp @@ -60,66 +60,72 @@ u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) { } template -void DecompressBlocks(std::span input, std::span output, Extent3D extent, +void DecompressBlocks(std::span input, std::span output, BufferImageCopy& copy, bool is_signed = false) { const u32 out_bpp = ConvertedBytesPerBlock(pixel_format); - const u32 block_width = std::min(extent.width, BLOCK_SIZE); - const u32 block_height = std::min(extent.height, BLOCK_SIZE); - const u32 pitch = extent.width * out_bpp; + const u32 block_size = BlockSize(pixel_format); + const u32 width = copy.image_extent.width; + const u32 height = copy.image_extent.height * copy.image_subresource.num_layers; + const u32 depth = copy.image_extent.depth; + const u32 block_width = std::min(width, BLOCK_SIZE); + const u32 block_height = std::min(height, BLOCK_SIZE); + const u32 pitch = width * out_bpp; size_t input_offset = 0; size_t output_offset = 0; - for (u32 slice = 0; slice < extent.depth; ++slice) { - for (u32 y = 0; y < extent.height; y += block_height) { - size_t row_offset = 0; - for (u32 x = 0; x < extent.width; - x += block_width, row_offset += block_width * out_bpp) { - const u8* src = input.data() + input_offset; - u8* const dst = output.data() + output_offset + row_offset; + for (u32 slice = 0; slice < depth; ++slice) { + for (u32 y = 0; y < height; y += block_height) { + size_t src_offset = input_offset; + size_t dst_offset = output_offset; + for (u32 x = 0; x < width; x += block_width) { + const u8* src = input.data() + src_offset; + u8* const dst = output.data() + dst_offset; if constexpr (IsSigned(pixel_format)) { - decompress(src, dst, x, y, extent.width, extent.height, is_signed); + decompress(src, dst, x, y, width, height, is_signed); } else { - decompress(src, dst, x, y, extent.width, extent.height); + decompress(src, dst, x, y, width, height); } - input_offset += BlockSize(pixel_format); + src_offset += block_size; + dst_offset += block_width * out_bpp; } + input_offset += copy.buffer_row_length * block_size / block_width; output_offset += block_height * pitch; } } } -void DecompressBCn(std::span input, std::span output, Extent3D extent, +void DecompressBCn(std::span input, std::span output, BufferImageCopy& copy, VideoCore::Surface::PixelFormat pixel_format) { switch (pixel_format) { case PixelFormat::BC1_RGBA_UNORM: case PixelFormat::BC1_RGBA_SRGB: - DecompressBlocks(input, output, extent); + DecompressBlocks(input, output, copy); break; case PixelFormat::BC2_UNORM: case PixelFormat::BC2_SRGB: - DecompressBlocks(input, output, extent); + DecompressBlocks(input, output, copy); break; case PixelFormat::BC3_UNORM: case PixelFormat::BC3_SRGB: - DecompressBlocks(input, output, extent); + DecompressBlocks(input, output, copy); break; case PixelFormat::BC4_SNORM: case PixelFormat::BC4_UNORM: DecompressBlocks( - input, output, extent, pixel_format == PixelFormat::BC4_SNORM); + input, output, copy, pixel_format == PixelFormat::BC4_SNORM); break; case PixelFormat::BC5_SNORM: case PixelFormat::BC5_UNORM: DecompressBlocks( - input, output, extent, pixel_format == PixelFormat::BC5_SNORM); + input, output, copy, pixel_format == PixelFormat::BC5_SNORM); break; case PixelFormat::BC6H_SFLOAT: case PixelFormat::BC6H_UFLOAT: DecompressBlocks( - input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT); + input, output, copy, pixel_format == PixelFormat::BC6H_SFLOAT); break; case PixelFormat::BC7_SRGB: case PixelFormat::BC7_UNORM: - DecompressBlocks(input, output, extent); + DecompressBlocks(input, output, copy); break; default: LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format); diff --git a/src/video_core/texture_cache/decode_bc.h b/src/video_core/texture_cache/decode_bc.h index 41d1ec0a3..4e3b9b8ac 100755 --- a/src/video_core/texture_cache/decode_bc.h +++ b/src/video_core/texture_cache/decode_bc.h @@ -13,7 +13,7 @@ namespace VideoCommon { [[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format); -void DecompressBCn(std::span input, std::span output, Extent3D extent, +void DecompressBCn(std::span input, std::span output, BufferImageCopy& copy, VideoCore::Surface::PixelFormat pixel_format); } // namespace VideoCommon diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index d9959baf3..75a8c4edf 100755 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -837,6 +837,7 @@ boost::container::small_vector UnswizzleImage(Tegra::Memory std::span output) { const size_t guest_size_bytes = input.size_bytes(); const u32 bpp_log2 = BytesPerBlockLog2(info.format); + const Extent2D tile_size = DefaultBlockSize(info.format); const Extent3D size = info.size; if (info.type == ImageType::Linear) { @@ -847,7 +848,7 @@ boost::container::small_vector UnswizzleImage(Tegra::Memory return {{ .buffer_offset = 0, .buffer_size = guest_size_bytes, - .buffer_row_length = info.pitch >> bpp_log2, + .buffer_row_length = info.pitch * tile_size.width >> bpp_log2, .buffer_image_height = size.height, .image_subresource = { @@ -862,7 +863,6 @@ boost::container::small_vector UnswizzleImage(Tegra::Memory const LevelInfo level_info = MakeLevelInfo(info); const s32 num_layers = info.resources.layers; const s32 num_levels = info.resources.levels; - const Extent2D tile_size = DefaultBlockSize(info.format); const std::array level_sizes = CalculateLevelSizes(level_info, num_levels); const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing); const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels); @@ -926,8 +926,6 @@ void ConvertImage(std::span input, const ImageInfo& info, std::span input, const ImageInfo& info, std::span(copy.buffer_size); } else { - const Extent3D image_extent{ - .width = copy.image_extent.width, - .height = copy.image_extent.height * copy.image_subresource.num_layers, - .depth = copy.image_extent.depth, - }; - DecompressBCn(input_offset, output.subspan(output_offset), image_extent, info.format); + DecompressBCn(input_offset, output.subspan(output_offset), copy, info.format); output_offset += copy.image_extent.width * copy.image_extent.height * copy.image_subresource.num_layers * ConvertedBytesPerBlock(info.format); } + + copy.buffer_row_length = mip_size.width; + copy.buffer_image_height = mip_size.height; } } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 8c9265dd1..0a494af27 100755 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -755,10 +755,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags // The wanted format is not supported by hardware, search for alternatives const VkFormat* alternatives = GetFormatAlternatives(wanted_format); if (alternatives == nullptr) { - ASSERT_MSG(false, - "Format={} with usage={} and type={} has no defined alternatives and host " - "hardware does not support it", - wanted_format, wanted_usage, format_type); + LOG_ERROR(Render_Vulkan, + "Format={} with usage={} and type={} has no defined alternatives and host " + "hardware does not support it", + wanted_format, wanted_usage, format_type); return wanted_format; } @@ -774,10 +774,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags } // No alternatives found, panic - ASSERT_MSG(false, - "Format={} with usage={} and type={} is not supported by the host hardware and " - "doesn't support any of the alternatives", - wanted_format, wanted_usage, format_type); + LOG_ERROR(Render_Vulkan, + "Format={} with usage={} and type={} is not supported by the host hardware and " + "doesn't support any of the alternatives", + wanted_format, wanted_usage, format_type); return wanted_format; } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index b33b3f277..965bd2e6a 100755 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -246,7 +246,9 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec .objectHandle = reinterpret_cast(handle), .pObjectName = name, }; - Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info)); + if (dld->vkSetDebugUtilsObjectNameEXT) { + Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info)); + } } } // Anonymous namespace diff --git a/src/yuzu/main.h b/src/yuzu/main.h index add1e3307..fb51e2659 100755 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -168,14 +168,6 @@ class GMainWindow : public QMainWindow { /// Max number of recently loaded items to keep track of static const int max_recent_files_item = 10; - // TODO: Make use of this! - enum { - UI_IDLE, - UI_EMU_BOOTING, - UI_EMU_RUNNING, - UI_EMU_STOPPING, - }; - enum { CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, CREATE_SHORTCUT_MSGBOX_SUCCESS,