early-access version 4035
This commit is contained in:
parent
78439b177e
commit
59fe352210
58 changed files with 834 additions and 363 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 4034.
|
This is the source code for early-access 4035.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,7 @@ constexpr auto INCREMENT_TIME{5ms};
|
||||||
DeviceSession::DeviceSession(Core::System& system_)
|
DeviceSession::DeviceSession(Core::System& system_)
|
||||||
: system{system_}, thread_event{Core::Timing::CreateEvent(
|
: system{system_}, thread_event{Core::Timing::CreateEvent(
|
||||||
"AudioOutSampleTick",
|
"AudioOutSampleTick",
|
||||||
[this](std::uintptr_t, s64 time, std::chrono::nanoseconds) {
|
[this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {}
|
||||||
return ThreadFunc();
|
|
||||||
})} {}
|
|
||||||
|
|
||||||
DeviceSession::~DeviceSession() {
|
DeviceSession::~DeviceSession() {
|
||||||
Finalize();
|
Finalize();
|
||||||
|
|
|
@ -64,6 +64,8 @@ add_library(common STATIC
|
||||||
fs/path_util.cpp
|
fs/path_util.cpp
|
||||||
fs/path_util.h
|
fs/path_util.h
|
||||||
hash.h
|
hash.h
|
||||||
|
heap_tracker.cpp
|
||||||
|
heap_tracker.h
|
||||||
hex_util.cpp
|
hex_util.cpp
|
||||||
hex_util.h
|
hex_util.h
|
||||||
host_memory.cpp
|
host_memory.cpp
|
||||||
|
|
|
@ -3,16 +3,19 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
|
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
|
||||||
void assert_fail_impl() {
|
void assert_fail_impl() {
|
||||||
if (Settings::values.use_debug_asserts) {
|
if (Settings::values.use_debug_asserts) {
|
||||||
|
Common::Log::Stop();
|
||||||
Crash();
|
Crash();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void unreachable_impl() {
|
[[noreturn]] void unreachable_impl() {
|
||||||
|
Common::Log::Stop();
|
||||||
Crash();
|
Crash();
|
||||||
throw std::runtime_error("Unreachable code");
|
throw std::runtime_error("Unreachable code");
|
||||||
}
|
}
|
||||||
|
|
263
src/common/heap_tracker.cpp
Executable file
263
src/common/heap_tracker.cpp
Executable file
|
@ -0,0 +1,263 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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
|
97
src/common/heap_tracker.h
Executable file
97
src/common/heap_tracker.h
Executable file
|
@ -0,0 +1,97 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
|
#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<SeparateHeapMapAddrComparator>;
|
||||||
|
|
||||||
|
using TickTreeTraits =
|
||||||
|
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
|
||||||
|
using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
|
||||||
|
|
||||||
|
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
|
|
@ -679,7 +679,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||||
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
|
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
|
||||||
|
|
||||||
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
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(virtual_offset % PageAlignment == 0);
|
||||||
ASSERT(host_offset % PageAlignment == 0);
|
ASSERT(host_offset % PageAlignment == 0);
|
||||||
ASSERT(length % 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);
|
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(virtual_offset % PageAlignment == 0);
|
||||||
ASSERT(length % PageAlignment == 0);
|
ASSERT(length % PageAlignment == 0);
|
||||||
ASSERT(virtual_offset + length <= virtual_size);
|
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);
|
impl->Unmap(virtual_offset + virtual_base_offset, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write,
|
void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
|
||||||
bool execute) {
|
|
||||||
ASSERT(virtual_offset % PageAlignment == 0);
|
ASSERT(virtual_offset % PageAlignment == 0);
|
||||||
ASSERT(length % PageAlignment == 0);
|
ASSERT(length % PageAlignment == 0);
|
||||||
ASSERT(virtual_offset + length <= virtual_size);
|
ASSERT(virtual_offset + length <= virtual_size);
|
||||||
if (length == 0 || !virtual_base || !impl) {
|
if (length == 0 || !virtual_base || !impl) {
|
||||||
return;
|
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);
|
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,12 @@ public:
|
||||||
HostMemory(HostMemory&& other) noexcept;
|
HostMemory(HostMemory&& other) noexcept;
|
||||||
HostMemory& operator=(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();
|
void EnableDirectMappedAddress();
|
||||||
|
|
||||||
|
@ -64,6 +65,10 @@ public:
|
||||||
return virtual_base;
|
return virtual_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsInVirtualRange(void* address) const noexcept {
|
||||||
|
return address >= virtual_base && address < virtual_base + virtual_size;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t backing_size{};
|
size_t backing_size{};
|
||||||
size_t virtual_size{};
|
size_t virtual_size{};
|
||||||
|
|
|
@ -208,6 +208,10 @@ public:
|
||||||
instance->StartBackendThread();
|
instance->StartBackendThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Stop() {
|
||||||
|
instance->StopBackendThread();
|
||||||
|
}
|
||||||
|
|
||||||
Impl(const Impl&) = delete;
|
Impl(const Impl&) = delete;
|
||||||
Impl& operator=(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,
|
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
||||||
const char* function, std::string&& message) const {
|
const char* function, std::string&& message) const {
|
||||||
using std::chrono::duration_cast;
|
using std::chrono::duration_cast;
|
||||||
|
@ -313,6 +326,10 @@ void Start() {
|
||||||
Impl::Start();
|
Impl::Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Stop() {
|
||||||
|
Impl::Stop();
|
||||||
|
}
|
||||||
|
|
||||||
void DisableLoggingInTests() {
|
void DisableLoggingInTests() {
|
||||||
initialization_in_progress_suppress_logging = true;
|
initialization_in_progress_suppress_logging = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ void Initialize();
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
|
/// Explicitly stops the logger thread and flushes the buffers
|
||||||
|
void Stop();
|
||||||
|
|
||||||
void DisableLoggingInTests();
|
void DisableLoggingInTests();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -103,7 +103,7 @@ private:
|
||||||
// Having them on the same cache-line would result in false-sharing between them.
|
// Having them on the same cache-line would result in false-sharing between them.
|
||||||
// TODO: Remove this ifdef whenever clang and GCC support
|
// TODO: Remove this ifdef whenever clang and GCC support
|
||||||
// std::hardware_destructive_interference_size.
|
// 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_read_index{0};
|
||||||
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
|
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -978,6 +978,7 @@ endif()
|
||||||
|
|
||||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||||
target_sources(core PRIVATE
|
target_sources(core PRIVATE
|
||||||
|
arm/dynarmic/arm_dynarmic.cpp
|
||||||
arm/dynarmic/arm_dynarmic.h
|
arm/dynarmic/arm_dynarmic.h
|
||||||
arm/dynarmic/arm_dynarmic_64.cpp
|
arm/dynarmic/arm_dynarmic_64.cpp
|
||||||
arm/dynarmic/arm_dynarmic_64.h
|
arm/dynarmic/arm_dynarmic_64.h
|
||||||
|
|
49
src/core/arm/dynarmic/arm_dynarmic.cpp
Executable file
49
src/core/arm/dynarmic/arm_dynarmic.cpp
Executable file
|
@ -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
|
|
@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
|
||||||
return static_cast<HaltReason>(hr);
|
return static_cast<HaltReason>(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
|
} // namespace Core
|
||||||
|
|
|
@ -331,11 +331,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
|
||||||
|
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Run());
|
return TranslateHaltReason(m_jit->Run());
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
|
||||||
|
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Step());
|
return TranslateHaltReason(m_jit->Step());
|
||||||
}
|
}
|
||||||
|
@ -377,6 +381,7 @@ ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProc
|
||||||
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
|
m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
|
||||||
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
|
auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
|
||||||
m_jit = MakeJit(&page_table_impl);
|
m_jit = MakeJit(&page_table_impl);
|
||||||
|
ScopedJitExecution::RegisterHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArmDynarmic32::~ArmDynarmic32() = default;
|
ArmDynarmic32::~ArmDynarmic32() = default;
|
||||||
|
|
|
@ -362,11 +362,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
|
||||||
|
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Run());
|
return TranslateHaltReason(m_jit->Run());
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
|
HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
|
||||||
|
ScopedJitExecution sj(thread->GetOwnerProcess());
|
||||||
|
|
||||||
m_jit->ClearExclusiveState();
|
m_jit->ClearExclusiveState();
|
||||||
return TranslateHaltReason(m_jit->Step());
|
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 = process->GetPageTable().GetBasePageTable();
|
||||||
auto& page_table_impl = page_table.GetImpl();
|
auto& page_table_impl = page_table.GetImpl();
|
||||||
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
|
m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
|
||||||
|
ScopedJitExecution::RegisterHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArmDynarmic64::~ArmDynarmic64() = default;
|
ArmDynarmic64::~ArmDynarmic64() = default;
|
||||||
|
|
|
@ -29,7 +29,6 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
|
||||||
struct CoreTiming::Event {
|
struct CoreTiming::Event {
|
||||||
s64 time;
|
s64 time;
|
||||||
u64 fifo_order;
|
u64 fifo_order;
|
||||||
std::uintptr_t user_data;
|
|
||||||
std::weak_ptr<EventType> type;
|
std::weak_ptr<EventType> type;
|
||||||
s64 reschedule_time;
|
s64 reschedule_time;
|
||||||
heap_t::handle_type handle{};
|
heap_t::handle_type handle{};
|
||||||
|
@ -67,17 +66,15 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||||
event_fifo_id = 0;
|
event_fifo_id = 0;
|
||||||
shutting_down = false;
|
shutting_down = false;
|
||||||
cpu_ticks = 0;
|
cpu_ticks = 0;
|
||||||
const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
|
|
||||||
-> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
|
|
||||||
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
|
||||||
if (is_multicore) {
|
if (is_multicore) {
|
||||||
timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
|
timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::ClearPendingEvents() {
|
void CoreTiming::ClearPendingEvents() {
|
||||||
std::scoped_lock lock{basic_lock};
|
std::scoped_lock lock{advance_lock, basic_lock};
|
||||||
event_queue.clear();
|
event_queue.clear();
|
||||||
|
event.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::Pause(bool is_paused) {
|
void CoreTiming::Pause(bool is_paused) {
|
||||||
|
@ -119,14 +116,12 @@ bool CoreTiming::HasPendingEvents() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||||
const std::shared_ptr<EventType>& event_type,
|
const std::shared_ptr<EventType>& event_type, bool absolute_time) {
|
||||||
std::uintptr_t user_data, bool absolute_time) {
|
|
||||||
{
|
{
|
||||||
std::scoped_lock scope{basic_lock};
|
std::scoped_lock scope{basic_lock};
|
||||||
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
|
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
|
||||||
|
|
||||||
auto h{event_queue.emplace(
|
auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, event_type, 0})};
|
||||||
Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})};
|
|
||||||
(*h).handle = h;
|
(*h).handle = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,13 +131,13 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||||
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||||
std::chrono::nanoseconds resched_time,
|
std::chrono::nanoseconds resched_time,
|
||||||
const std::shared_ptr<EventType>& event_type,
|
const std::shared_ptr<EventType>& event_type,
|
||||||
std::uintptr_t user_data, bool absolute_time) {
|
bool absolute_time) {
|
||||||
{
|
{
|
||||||
std::scoped_lock scope{basic_lock};
|
std::scoped_lock scope{basic_lock};
|
||||||
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
|
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,
|
auto h{event_queue.emplace(
|
||||||
resched_time.count()})};
|
Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()})};
|
||||||
(*h).handle = h;
|
(*h).handle = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,14 +145,14 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||||
std::uintptr_t user_data, bool wait) {
|
UnscheduleEventType type) {
|
||||||
{
|
{
|
||||||
std::scoped_lock lk{basic_lock};
|
std::scoped_lock lk{basic_lock};
|
||||||
|
|
||||||
std::vector<heap_t::handle_type> to_remove;
|
std::vector<heap_t::handle_type> to_remove;
|
||||||
for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
|
for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
|
||||||
const Event& e = *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);
|
to_remove.push_back(itr->handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,10 +160,12 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||||
for (auto h : to_remove) {
|
for (auto h : to_remove) {
|
||||||
event_queue.erase(h);
|
event_queue.erase(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event_type->sequence_number++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force any in-progress events to finish
|
// Force any in-progress events to finish
|
||||||
if (wait) {
|
if (type == UnscheduleEventType::Wait) {
|
||||||
std::scoped_lock lk{advance_lock};
|
std::scoped_lock lk{advance_lock};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,28 +205,31 @@ std::optional<s64> CoreTiming::Advance() {
|
||||||
const Event& evt = event_queue.top();
|
const Event& evt = event_queue.top();
|
||||||
|
|
||||||
if (const auto event_type{evt.type.lock()}) {
|
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();
|
event_queue.pop();
|
||||||
|
|
||||||
basic_lock.unlock();
|
basic_lock.unlock();
|
||||||
|
|
||||||
event_type->callback(
|
event_type->callback(
|
||||||
evt_user_data, evt_time,
|
evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
|
||||||
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
|
|
||||||
|
|
||||||
basic_lock.lock();
|
basic_lock.lock();
|
||||||
} else {
|
} else {
|
||||||
basic_lock.unlock();
|
basic_lock.unlock();
|
||||||
|
|
||||||
const auto new_schedule_time{event_type->callback(
|
const auto new_schedule_time{event_type->callback(
|
||||||
evt.user_data, evt.time,
|
evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time})};
|
||||||
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
|
|
||||||
|
|
||||||
basic_lock.lock();
|
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()
|
const auto next_schedule_time{new_schedule_time.has_value()
|
||||||
? new_schedule_time.value().count()
|
? new_schedule_time.value().count()
|
||||||
: evt.reschedule_time};
|
: evt.reschedule_time};
|
||||||
|
@ -241,8 +241,8 @@ std::optional<s64> CoreTiming::Advance() {
|
||||||
next_time = pause_end_time + next_schedule_time;
|
next_time = pause_end_time + next_schedule_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data,
|
event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type,
|
||||||
evt.type, next_schedule_time, evt.handle});
|
next_schedule_time, evt.handle});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,17 +22,25 @@ namespace Core::Timing {
|
||||||
|
|
||||||
/// A callback that may be scheduled for a particular core timing event.
|
/// A callback that may be scheduled for a particular core timing event.
|
||||||
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
|
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
|
||||||
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.
|
/// Contains the characteristics of a particular event.
|
||||||
struct EventType {
|
struct EventType {
|
||||||
explicit EventType(TimedCallback&& callback_, std::string&& name_)
|
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.
|
/// The event's callback function.
|
||||||
TimedCallback callback;
|
TimedCallback callback;
|
||||||
/// A pointer to the name of the event.
|
/// A pointer to the name of the event.
|
||||||
const std::string name;
|
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
|
/// Schedules an event in core timing
|
||||||
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||||
const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0,
|
const std::shared_ptr<EventType>& event_type, bool absolute_time = false);
|
||||||
bool absolute_time = false);
|
|
||||||
|
|
||||||
/// Schedules an event which will automatically re-schedule itself with the given time, until
|
/// Schedules an event which will automatically re-schedule itself with the given time, until
|
||||||
/// unscheduled
|
/// unscheduled
|
||||||
void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||||
std::chrono::nanoseconds resched_time,
|
std::chrono::nanoseconds resched_time,
|
||||||
const std::shared_ptr<EventType>& event_type,
|
const std::shared_ptr<EventType>& event_type,
|
||||||
std::uintptr_t user_data = 0, bool absolute_time = false);
|
bool absolute_time = false);
|
||||||
|
|
||||||
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data,
|
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
|
||||||
bool wait = true);
|
UnscheduleEventType type = UnscheduleEventType::Wait);
|
||||||
|
|
||||||
void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
|
|
||||||
std::uintptr_t user_data) {
|
|
||||||
UnscheduleEvent(event_type, user_data, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddTicks(u64 ticks_to_add);
|
void AddTicks(u64 ticks_to_add);
|
||||||
|
|
||||||
|
@ -158,7 +160,6 @@ private:
|
||||||
heap_t event_queue;
|
heap_t event_queue;
|
||||||
u64 event_fifo_id = 0;
|
u64 event_fifo_id = 0;
|
||||||
|
|
||||||
std::shared_ptr<EventType> ev_lost;
|
|
||||||
Common::Event event{};
|
Common::Event event{};
|
||||||
Common::Event pause_event{};
|
Common::Event pause_event{};
|
||||||
mutable std::mutex basic_lock;
|
mutable std::mutex basic_lock;
|
||||||
|
|
|
@ -8,19 +8,22 @@
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
|
void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
|
||||||
KScopedLightLock lk(m_lock);
|
// KScopedInterruptDisable di;
|
||||||
|
KScopedSpinLock lk(m_lock);
|
||||||
|
|
||||||
m_object_list.insert_unique(*obj);
|
m_object_list.insert_unique(*obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
|
void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
|
||||||
KScopedLightLock lk(m_lock);
|
// KScopedInterruptDisable di;
|
||||||
|
KScopedSpinLock lk(m_lock);
|
||||||
|
|
||||||
m_object_list.erase(*obj);
|
m_object_list.erase(*obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
|
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(),
|
return std::count_if(m_object_list.begin(), m_object_list.end(),
|
||||||
[&](const auto& obj) { return obj.GetOwner() == owner; });
|
[&](const auto& obj) { return obj.GetOwner() == owner; });
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "core/hle/kernel/k_auto_object.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 {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -21,32 +21,7 @@ public:
|
||||||
|
|
||||||
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
|
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
|
||||||
|
|
||||||
class ListAccessor : public KScopedLightLock {
|
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {}
|
||||||
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() {}
|
|
||||||
|
|
||||||
void Initialize() {}
|
void Initialize() {}
|
||||||
void Finalize() {}
|
void Finalize() {}
|
||||||
|
@ -56,7 +31,7 @@ public:
|
||||||
size_t GetOwnedCount(KProcess* owner);
|
size_t GetOwnedCount(KProcess* owner);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KLightLock m_lock;
|
KSpinLock m_lock;
|
||||||
ListType m_object_list;
|
ListType m_object_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
public:
|
public:
|
||||||
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
|
||||||
|
|
||||||
Result Initialize(KProcess* owner, s32 size) {
|
Result Initialize(s32 size) {
|
||||||
// Check that the table size is valid.
|
// Check that the table size is valid.
|
||||||
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ public:
|
||||||
m_next_linear_id = MinLinearId;
|
m_next_linear_id = MinLinearId;
|
||||||
m_count = 0;
|
m_count = 0;
|
||||||
m_free_head_index = -1;
|
m_free_head_index = -1;
|
||||||
m_owner = owner;
|
|
||||||
|
|
||||||
// Free all entries.
|
// Free all entries.
|
||||||
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
|
for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
|
||||||
|
@ -91,8 +90,7 @@ public:
|
||||||
// Handle pseudo-handles.
|
// Handle pseudo-handles.
|
||||||
if constexpr (std::derived_from<KProcess, T>) {
|
if constexpr (std::derived_from<KProcess, T>) {
|
||||||
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
if (handle == Svc::PseudoHandle::CurrentProcess) {
|
||||||
// TODO: this should be the current process
|
auto* const cur_process = GetCurrentProcessPointer(m_kernel);
|
||||||
auto* const cur_process = m_owner;
|
|
||||||
ASSERT(cur_process != nullptr);
|
ASSERT(cur_process != nullptr);
|
||||||
return cur_process;
|
return cur_process;
|
||||||
}
|
}
|
||||||
|
@ -302,7 +300,6 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KernelCore& m_kernel;
|
KernelCore& m_kernel;
|
||||||
KProcess* m_owner{};
|
|
||||||
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
|
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
|
||||||
std::array<KAutoObject*, MaxTableSize> m_objects{};
|
std::array<KAutoObject*, MaxTableSize> m_objects{};
|
||||||
mutable KSpinLock m_lock;
|
mutable KSpinLock m_lock;
|
||||||
|
|
|
@ -10,15 +10,15 @@ namespace Kernel {
|
||||||
|
|
||||||
void KHardwareTimer::Initialize() {
|
void KHardwareTimer::Initialize() {
|
||||||
// Create the timing callback to register with CoreTiming.
|
// Create the timing callback to register with CoreTiming.
|
||||||
m_event_type = Core::Timing::CreateEvent(
|
m_event_type = Core::Timing::CreateEvent("KHardwareTimer::Callback",
|
||||||
"KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) {
|
[this](s64, std::chrono::nanoseconds) {
|
||||||
reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask();
|
this->DoTask();
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void KHardwareTimer::Finalize() {
|
void KHardwareTimer::Finalize() {
|
||||||
m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
|
m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type);
|
||||||
m_wakeup_time = std::numeric_limits<s64>::max();
|
m_wakeup_time = std::numeric_limits<s64>::max();
|
||||||
m_event_type.reset();
|
m_event_type.reset();
|
||||||
}
|
}
|
||||||
|
@ -57,13 +57,12 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
|
||||||
|
|
||||||
m_wakeup_time = wakeup_time;
|
m_wakeup_time = wakeup_time;
|
||||||
m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
|
m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
|
||||||
m_event_type, reinterpret_cast<uintptr_t>(this),
|
m_event_type, true);
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KHardwareTimer::DisableInterrupt() {
|
void KHardwareTimer::DisableInterrupt() {
|
||||||
m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type,
|
m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type,
|
||||||
reinterpret_cast<uintptr_t>(this));
|
Core::Timing::UnscheduleEventType::NoWait);
|
||||||
m_wakeup_time = std::numeric_limits<s64>::max();
|
m_wakeup_time = std::numeric_limits<s64>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -434,7 +434,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
|
||||||
void KPageTableBase::Finalize() {
|
void KPageTableBase::Finalize() {
|
||||||
auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
|
auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
|
||||||
if (Settings::IsFastmemEnabled()) {
|
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.
|
// Unmap.
|
||||||
R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
|
R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
|
||||||
cur_pages, 0, false, unmap_properties,
|
cur_pages, 0, false, unmap_properties,
|
||||||
OperationType::Unmap, true));
|
OperationType::UnmapPhysical, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we're done.
|
// Check if we're done.
|
||||||
|
@ -5326,7 +5326,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||||
// Map the papges.
|
// Map the papges.
|
||||||
R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
|
R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
|
||||||
cur_pg, map_properties,
|
cur_pg, map_properties,
|
||||||
OperationType::MapFirstGroup, false));
|
OperationType::MapFirstGroupPhysical, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5480,7 +5480,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size)
|
||||||
|
|
||||||
// Unmap.
|
// Unmap.
|
||||||
R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
|
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.
|
// 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).
|
// or free them to the page list, and so it goes unused (along with page properties).
|
||||||
|
|
||||||
switch (operation) {
|
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.
|
// Ensure that any pages we track are closed on exit.
|
||||||
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
|
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
|
||||||
SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
|
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);
|
this->MakePageGroup(pages_to_close, virt_addr, num_pages);
|
||||||
|
|
||||||
// Unmap.
|
// 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();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
@ -5672,7 +5675,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||||
ASSERT(virt_addr != 0);
|
ASSERT(virt_addr != 0);
|
||||||
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
|
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
|
||||||
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
|
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.
|
// Open references to pages, if we should.
|
||||||
if (this->IsHeapPhysicalAddress(phys_addr)) {
|
if (this->IsHeapPhysicalAddress(phys_addr)) {
|
||||||
|
@ -5711,16 +5714,19 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
|
||||||
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case OperationType::MapGroup:
|
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.
|
// 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) {
|
for (const auto& node : page_group) {
|
||||||
const size_t size{node.GetNumPages() * PageSize};
|
const size_t size{node.GetNumPages() * PageSize};
|
||||||
|
|
||||||
// Map the pages.
|
// Map the pages.
|
||||||
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
|
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
|
||||||
ConvertToMemoryPermission(properties.perm));
|
ConvertToMemoryPermission(properties.perm), separate_heap);
|
||||||
|
|
||||||
virt_addr += size;
|
virt_addr += size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,9 @@ protected:
|
||||||
ChangePermissionsAndRefresh = 5,
|
ChangePermissionsAndRefresh = 5,
|
||||||
ChangePermissionsAndRefreshAndFlush = 6,
|
ChangePermissionsAndRefreshAndFlush = 6,
|
||||||
Separate = 7,
|
Separate = 7,
|
||||||
|
|
||||||
|
MapFirstGroupPhysical = 65000,
|
||||||
|
UnmapPhysical = 65001,
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
|
static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
|
||||||
|
|
|
@ -1237,8 +1237,10 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
|
||||||
auto& buffer = m_kernel.System().DeviceMemory().buffer;
|
auto& buffer = m_kernel.System().DeviceMemory().buffer;
|
||||||
const auto& code = code_set.CodeSegment();
|
const auto& code = code_set.CodeSegment();
|
||||||
const auto& patch = code_set.PatchSegment();
|
const auto& patch = code_set.PatchSegment();
|
||||||
buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true);
|
buffer.Protect(GetInteger(base_addr + code.addr), code.size,
|
||||||
buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true);
|
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);
|
ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -552,7 +552,7 @@ private:
|
||||||
|
|
||||||
Result InitializeHandleTable(s32 size) {
|
Result InitializeHandleTable(s32 size) {
|
||||||
// Try to initialize the handle table.
|
// 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.
|
// We succeeded, so note that we did.
|
||||||
m_is_handle_table_initialized = true;
|
m_is_handle_table_initialized = true;
|
||||||
|
|
|
@ -1147,8 +1147,7 @@ Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server
|
||||||
*out_context =
|
*out_context =
|
||||||
std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
|
std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
|
||||||
(*out_context)->SetSessionRequestManager(manager);
|
(*out_context)->SetSessionRequestManager(manager);
|
||||||
(*out_context)
|
(*out_context)->PopulateFromIncomingCommandBuffer(cmd_buf);
|
||||||
->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf);
|
|
||||||
// We succeeded.
|
// We succeeded.
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include "core/hle/kernel/k_light_lock.h"
|
||||||
#include "core/hle/kernel/k_page_group.h"
|
#include "core/hle/kernel/k_page_group.h"
|
||||||
#include "core/hle/kernel/slab_helpers.h"
|
#include "core/hle/kernel/slab_helpers.h"
|
||||||
#include "core/hle/kernel/svc_types.h"
|
#include "core/hle/kernel/svc_types.h"
|
||||||
|
|
|
@ -238,7 +238,7 @@ struct KernelCore::Impl {
|
||||||
void InitializePreemption(KernelCore& kernel) {
|
void InitializePreemption(KernelCore& kernel) {
|
||||||
preemption_event = Core::Timing::CreateEvent(
|
preemption_event = Core::Timing::CreateEvent(
|
||||||
"PreemptionCallback",
|
"PreemptionCallback",
|
||||||
[this, &kernel](std::uintptr_t, s64 time,
|
[this, &kernel](s64 time,
|
||||||
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
||||||
{
|
{
|
||||||
KScopedSchedulerLock lock(kernel);
|
KScopedSchedulerLock lock(kernel);
|
||||||
|
|
|
@ -1513,8 +1513,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto transfer_mem =
|
auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
|
||||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
|
|
||||||
|
|
||||||
if (transfer_mem.IsNull()) {
|
if (transfer_mem.IsNull()) {
|
||||||
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
|
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
|
||||||
|
@ -1524,8 +1523,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> memory(transfer_mem->GetSize());
|
std::vector<u8> memory(transfer_mem->GetSize());
|
||||||
system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(),
|
ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
|
||||||
memory.size());
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
@ -1547,8 +1545,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto transfer_mem =
|
auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
|
||||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
|
|
||||||
|
|
||||||
if (transfer_mem.IsNull()) {
|
if (transfer_mem.IsNull()) {
|
||||||
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
|
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
|
||||||
|
@ -1558,8 +1555,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> memory(transfer_mem->GetSize());
|
std::vector<u8> memory(transfer_mem->GetSize());
|
||||||
system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(),
|
ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
|
||||||
memory.size());
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
|
|
@ -454,10 +454,8 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
|
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
|
||||||
auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
|
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||||
auto transfer_memory{
|
|
||||||
process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
|
|
||||||
|
|
||||||
const auto session_id{impl->GetSessionId()};
|
const auto session_id{impl->GetSessionId()};
|
||||||
if (session_id == -1) {
|
if (session_id == -1) {
|
||||||
|
|
|
@ -278,9 +278,7 @@ void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) {
|
||||||
auto params = rp.PopRaw<OpusParameters>();
|
auto params = rp.PopRaw<OpusParameters>();
|
||||||
auto transfer_memory_size{rp.Pop<u32>()};
|
auto transfer_memory_size{rp.Pop<u32>()};
|
||||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||||
auto transfer_memory{
|
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
|
||||||
transfer_memory_handle)};
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
|
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
|
||||||
params.sample_rate, params.channel_count, transfer_memory_size);
|
params.sample_rate, params.channel_count, transfer_memory_size);
|
||||||
|
@ -323,9 +321,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
|
||||||
|
|
||||||
auto transfer_memory_size{rp.Pop<u32>()};
|
auto transfer_memory_size{rp.Pop<u32>()};
|
||||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||||
auto transfer_memory{
|
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
|
||||||
transfer_memory_handle)};
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_Audio,
|
LOG_DEBUG(Service_Audio,
|
||||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||||
|
@ -374,9 +370,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
|
||||||
auto params = rp.PopRaw<OpusParametersEx>();
|
auto params = rp.PopRaw<OpusParametersEx>();
|
||||||
auto transfer_memory_size{rp.Pop<u32>()};
|
auto transfer_memory_size{rp.Pop<u32>()};
|
||||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||||
auto transfer_memory{
|
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
|
||||||
transfer_memory_handle)};
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
|
LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
|
||||||
params.sample_rate, params.channel_count, transfer_memory_size);
|
params.sample_rate, params.channel_count, transfer_memory_size);
|
||||||
|
@ -414,9 +408,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
|
||||||
|
|
||||||
auto transfer_memory_size{rp.Pop<u32>()};
|
auto transfer_memory_size{rp.Pop<u32>()};
|
||||||
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
auto transfer_memory_handle{ctx.GetCopyHandle(0)};
|
||||||
auto transfer_memory{
|
auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
|
||||||
system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
|
||||||
transfer_memory_handle)};
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_Audio,
|
LOG_DEBUG(Service_Audio,
|
||||||
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
"sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
|
||||||
|
|
|
@ -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_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");
|
ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
|
||||||
|
|
||||||
auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
auto t_mem_1 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_1_handle);
|
||||||
t_mem_1_handle);
|
|
||||||
|
|
||||||
if (t_mem_1.IsNull()) {
|
if (t_mem_1.IsNull()) {
|
||||||
LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
auto t_mem_2 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_2_handle);
|
||||||
t_mem_2_handle);
|
|
||||||
|
|
||||||
if (t_mem_2.IsNull()) {
|
if (t_mem_2.IsNull()) {
|
||||||
LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
|
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");
|
ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
|
||||||
|
|
||||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||||
t_mem_handle);
|
|
||||||
|
|
||||||
if (t_mem.IsNull()) {
|
if (t_mem.IsNull()) {
|
||||||
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
||||||
|
|
|
@ -49,10 +49,10 @@ HidBus::HidBus(Core::System& system_)
|
||||||
// Register update callbacks
|
// Register update callbacks
|
||||||
hidbus_update_event = Core::Timing::CreateEvent(
|
hidbus_update_event = Core::Timing::CreateEvent(
|
||||||
"Hidbus::UpdateCallback",
|
"Hidbus::UpdateCallback",
|
||||||
[this](std::uintptr_t user_data, s64 time,
|
[this](s64 time,
|
||||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
const auto guard = LockService();
|
const auto guard = LockService();
|
||||||
UpdateHidbus(user_data, ns_late);
|
UpdateHidbus(ns_late);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -61,10 +61,10 @@ HidBus::HidBus(Core::System& system_)
|
||||||
}
|
}
|
||||||
|
|
||||||
HidBus::~HidBus() {
|
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) {
|
if (is_hidbus_enabled) {
|
||||||
for (std::size_t i = 0; i < devices.size(); ++i) {
|
for (std::size_t i = 0; i < devices.size(); ++i) {
|
||||||
if (!devices[i].is_device_initializated) {
|
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");
|
ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
|
||||||
|
|
||||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||||
t_mem_handle);
|
|
||||||
|
|
||||||
if (t_mem.IsNull()) {
|
if (t_mem.IsNull()) {
|
||||||
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
||||||
|
|
|
@ -108,7 +108,7 @@ private:
|
||||||
void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
|
void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
|
||||||
void SetStatusManagerType(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<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
|
std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -197,8 +197,7 @@ void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
||||||
|
|
||||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||||
t_mem_handle);
|
|
||||||
|
|
||||||
if (t_mem.IsNull()) {
|
if (t_mem.IsNull()) {
|
||||||
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
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<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
const auto t_mem_handle{ctx.GetCopyHandle(0)};
|
||||||
|
|
||||||
auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
|
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
|
||||||
t_mem_handle);
|
|
||||||
|
|
||||||
LOG_INFO(Service_IRS,
|
LOG_INFO(Service_IRS,
|
||||||
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
||||||
|
|
|
@ -227,8 +227,7 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
|
||||||
applet_resource->EnableTouchScreen(aruid, is_enabled);
|
applet_resource->EnableTouchScreen(aruid, is_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceManager::UpdateControllers(std::uintptr_t user_data,
|
void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
|
||||||
std::chrono::nanoseconds ns_late) {
|
|
||||||
auto& core_timing = system.CoreTiming();
|
auto& core_timing = system.CoreTiming();
|
||||||
debug_pad->OnUpdate(core_timing);
|
debug_pad->OnUpdate(core_timing);
|
||||||
digitizer->OnUpdate(core_timing);
|
digitizer->OnUpdate(core_timing);
|
||||||
|
@ -241,20 +240,19 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
|
||||||
capture_button->OnUpdate(core_timing);
|
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();
|
auto& core_timing = system.CoreTiming();
|
||||||
npad->OnUpdate(core_timing);
|
npad->OnUpdate(core_timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
|
void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) {
|
||||||
std::chrono::nanoseconds ns_late) {
|
|
||||||
auto& core_timing = system.CoreTiming();
|
auto& core_timing = system.CoreTiming();
|
||||||
mouse->OnUpdate(core_timing);
|
mouse->OnUpdate(core_timing);
|
||||||
debug_mouse->OnUpdate(core_timing);
|
debug_mouse->OnUpdate(core_timing);
|
||||||
keyboard->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();
|
auto& core_timing = system.CoreTiming();
|
||||||
six_axis->OnUpdate(core_timing);
|
six_axis->OnUpdate(core_timing);
|
||||||
seven_six_axis->OnUpdate(core_timing);
|
seven_six_axis->OnUpdate(core_timing);
|
||||||
|
@ -273,34 +271,34 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
|
||||||
// Register update callbacks
|
// Register update callbacks
|
||||||
npad_update_event = Core::Timing::CreateEvent(
|
npad_update_event = Core::Timing::CreateEvent(
|
||||||
"HID::UpdatePadCallback",
|
"HID::UpdatePadCallback",
|
||||||
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
[this, resource](
|
||||||
-> std::optional<std::chrono::nanoseconds> {
|
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
const auto guard = LockService();
|
const auto guard = LockService();
|
||||||
resource->UpdateNpad(user_data, ns_late);
|
resource->UpdateNpad(ns_late);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
});
|
});
|
||||||
default_update_event = Core::Timing::CreateEvent(
|
default_update_event = Core::Timing::CreateEvent(
|
||||||
"HID::UpdateDefaultCallback",
|
"HID::UpdateDefaultCallback",
|
||||||
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
[this, resource](
|
||||||
-> std::optional<std::chrono::nanoseconds> {
|
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
const auto guard = LockService();
|
const auto guard = LockService();
|
||||||
resource->UpdateControllers(user_data, ns_late);
|
resource->UpdateControllers(ns_late);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
});
|
});
|
||||||
mouse_keyboard_update_event = Core::Timing::CreateEvent(
|
mouse_keyboard_update_event = Core::Timing::CreateEvent(
|
||||||
"HID::UpdateMouseKeyboardCallback",
|
"HID::UpdateMouseKeyboardCallback",
|
||||||
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
[this, resource](
|
||||||
-> std::optional<std::chrono::nanoseconds> {
|
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
const auto guard = LockService();
|
const auto guard = LockService();
|
||||||
resource->UpdateMouseKeyboard(user_data, ns_late);
|
resource->UpdateMouseKeyboard(ns_late);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
});
|
});
|
||||||
motion_update_event = Core::Timing::CreateEvent(
|
motion_update_event = Core::Timing::CreateEvent(
|
||||||
"HID::UpdateMotionCallback",
|
"HID::UpdateMotionCallback",
|
||||||
[this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
|
[this, resource](
|
||||||
-> std::optional<std::chrono::nanoseconds> {
|
s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
const auto guard = LockService();
|
const auto guard = LockService();
|
||||||
resource->UpdateMotion(user_data, ns_late);
|
resource->UpdateMotion(ns_late);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -314,10 +312,10 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
|
||||||
}
|
}
|
||||||
|
|
||||||
IAppletResource::~IAppletResource() {
|
IAppletResource::~IAppletResource() {
|
||||||
system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
|
system.CoreTiming().UnscheduleEvent(npad_update_event);
|
||||||
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
|
system.CoreTiming().UnscheduleEvent(default_update_event);
|
||||||
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
|
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
|
||||||
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
|
system.CoreTiming().UnscheduleEvent(motion_update_event);
|
||||||
resource_manager->FreeAppletResourceId(aruid);
|
resource_manager->FreeAppletResourceId(aruid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,10 +81,10 @@ public:
|
||||||
void EnablePadInput(u64 aruid, bool is_enabled);
|
void EnablePadInput(u64 aruid, bool is_enabled);
|
||||||
void EnableTouchScreen(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 UpdateControllers(std::chrono::nanoseconds ns_late);
|
||||||
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
void UpdateNpad(std::chrono::nanoseconds ns_late);
|
||||||
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
|
||||||
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
void UpdateMotion(std::chrono::nanoseconds ns_late);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result CreateAppletResourceImpl(u64 aruid);
|
Result CreateAppletResourceImpl(u64 aruid);
|
||||||
|
|
|
@ -146,10 +146,7 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::
|
||||||
|
|
||||||
HLERequestContext::~HLERequestContext() = default;
|
HLERequestContext::~HLERequestContext() = default;
|
||||||
|
|
||||||
void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf,
|
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||||
bool incoming) {
|
|
||||||
client_handle_table = &process.GetHandleTable();
|
|
||||||
|
|
||||||
IPC::RequestParser rp(src_cmdbuf);
|
IPC::RequestParser rp(src_cmdbuf);
|
||||||
command_header = rp.PopRaw<IPC::CommandHeader>();
|
command_header = rp.PopRaw<IPC::CommandHeader>();
|
||||||
|
|
||||||
|
@ -162,7 +159,7 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
|
||||||
if (command_header->enable_handle_descriptor) {
|
if (command_header->enable_handle_descriptor) {
|
||||||
handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
|
handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
|
||||||
if (handle_descriptor_header->send_current_pid) {
|
if (handle_descriptor_header->send_current_pid) {
|
||||||
pid = process.GetProcessId();
|
pid = thread->GetOwnerProcess()->GetProcessId();
|
||||||
rp.Skip(2, false);
|
rp.Skip(2, false);
|
||||||
}
|
}
|
||||||
if (incoming) {
|
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.
|
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process,
|
Result HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf) {
|
||||||
u32_le* src_cmdbuf) {
|
client_handle_table = &thread->GetOwnerProcess()->GetHandleTable();
|
||||||
ParseCommandBuffer(process, src_cmdbuf, true);
|
|
||||||
|
ParseCommandBuffer(src_cmdbuf, true);
|
||||||
|
|
||||||
if (command_header->IsCloseCommand()) {
|
if (command_header->IsCloseCommand()) {
|
||||||
// Close does not populate the rest of the IPC header
|
// Close does not populate the rest of the IPC header
|
||||||
|
@ -284,9 +282,9 @@ Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& pr
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread) {
|
Result HLERequestContext::WriteToOutgoingCommandBuffer() {
|
||||||
auto current_offset = handles_offset;
|
auto current_offset = handles_offset;
|
||||||
auto& owner_process = *requesting_thread.GetOwnerProcess();
|
auto& owner_process = *thread->GetOwnerProcess();
|
||||||
auto& handle_table = owner_process.GetHandleTable();
|
auto& handle_table = owner_process.GetHandleTable();
|
||||||
|
|
||||||
for (auto& object : outgoing_copy_objects) {
|
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.
|
// 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;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "common/concepts.h"
|
#include "common/concepts.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/hle/ipc.h"
|
#include "core/hle/ipc.h"
|
||||||
|
#include "core/hle/kernel/k_handle_table.h"
|
||||||
#include "core/hle/kernel/svc_common.h"
|
#include "core/hle/kernel/svc_common.h"
|
||||||
|
|
||||||
union Result;
|
union Result;
|
||||||
|
@ -196,10 +197,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Populates this context with data from the requesting process/thread.
|
/// 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.
|
/// Writes data from this context back to the requesting process/thread.
|
||||||
Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread);
|
Result WriteToOutgoingCommandBuffer();
|
||||||
|
|
||||||
[[nodiscard]] u32_le GetHipcCommand() const {
|
[[nodiscard]] u32_le GetHipcCommand() const {
|
||||||
return command;
|
return command;
|
||||||
|
@ -359,8 +360,17 @@ public:
|
||||||
return *thread;
|
return *thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::KHandleTable& GetClientHandleTable() {
|
[[nodiscard]] Core::Memory::Memory& GetMemory() const {
|
||||||
return *client_handle_table;
|
return memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Kernel::KScopedAutoObject<T> GetObjectFromHandle(u32 handle) {
|
||||||
|
auto obj = client_handle_table->GetObjectForIpc(handle, thread);
|
||||||
|
if (obj.IsNotNull()) {
|
||||||
|
return obj->DynamicCast<T*>();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
|
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
|
||||||
|
@ -378,7 +388,7 @@ public:
|
||||||
private:
|
private:
|
||||||
friend class IPC::ResponseBuilder;
|
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<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||||
Kernel::KServerSession* server_session{};
|
Kernel::KServerSession* server_session{};
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx,
|
explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx,
|
||||||
CodeRange user_ro)
|
CodeRange user_ro)
|
||||||
: ServiceFramework{system_, "IJitEnvironment"}, process{&process_},
|
: ServiceFramework{system_, "IJitEnvironment"}, process{&process_},
|
||||||
context{system_.ApplicationMemory()} {
|
context{process->GetMemory()} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IJitEnvironment::GenerateCode, "GenerateCode"},
|
{0, &IJitEnvironment::GenerateCode, "GenerateCode"},
|
||||||
|
@ -188,7 +188,7 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)};
|
auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
|
||||||
if (tmem.IsNull()) {
|
if (tmem.IsNull()) {
|
||||||
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
|
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
@ -356,11 +356,7 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch using the handle table for the application process here,
|
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
|
||||||
// since we are not multiprocess yet.
|
|
||||||
const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
|
|
||||||
|
|
||||||
auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
|
|
||||||
if (process.IsNull()) {
|
if (process.IsNull()) {
|
||||||
LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
|
LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
@ -368,7 +364,7 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)};
|
auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
|
||||||
if (rx_mem.IsNull()) {
|
if (rx_mem.IsNull()) {
|
||||||
LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
|
LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
@ -376,7 +372,7 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)};
|
auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)};
|
||||||
if (ro_mem.IsNull()) {
|
if (ro_mem.IsNull()) {
|
||||||
LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
|
LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
|
|
@ -67,7 +67,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
|
||||||
// Schedule the screen composition events
|
// Schedule the screen composition events
|
||||||
multi_composition_event = Core::Timing::CreateEvent(
|
multi_composition_event = Core::Timing::CreateEvent(
|
||||||
"ScreenComposition",
|
"ScreenComposition",
|
||||||
[this](std::uintptr_t, s64 time,
|
[this](s64 time,
|
||||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
vsync_signal.Set();
|
vsync_signal.Set();
|
||||||
return std::chrono::nanoseconds(GetNextTicks());
|
return std::chrono::nanoseconds(GetNextTicks());
|
||||||
|
@ -75,7 +75,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
|
||||||
|
|
||||||
single_composition_event = Core::Timing::CreateEvent(
|
single_composition_event = Core::Timing::CreateEvent(
|
||||||
"ScreenComposition",
|
"ScreenComposition",
|
||||||
[this](std::uintptr_t, s64 time,
|
[this](s64 time,
|
||||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
const auto lock_guard = Lock();
|
const auto lock_guard = Lock();
|
||||||
Compose();
|
Compose();
|
||||||
|
@ -93,11 +93,11 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
|
||||||
|
|
||||||
Nvnflinger::~Nvnflinger() {
|
Nvnflinger::~Nvnflinger() {
|
||||||
if (system.IsMulticore()) {
|
if (system.IsMulticore()) {
|
||||||
system.CoreTiming().UnscheduleEvent(multi_composition_event, {});
|
system.CoreTiming().UnscheduleEvent(multi_composition_event);
|
||||||
vsync_thread.request_stop();
|
vsync_thread.request_stop();
|
||||||
vsync_signal.Set();
|
vsync_signal.Set();
|
||||||
} else {
|
} else {
|
||||||
system.CoreTiming().UnscheduleEvent(single_composition_event, {});
|
system.CoreTiming().UnscheduleEvent(single_composition_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShutdownLayers();
|
ShutdownLayers();
|
||||||
|
|
|
@ -651,10 +651,9 @@ private:
|
||||||
void RegisterProcessHandle(HLERequestContext& ctx) {
|
void RegisterProcessHandle(HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_LDR, "(called)");
|
LOG_DEBUG(Service_LDR, "(called)");
|
||||||
|
|
||||||
auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
|
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
|
||||||
auto client_pid = ctx.GetPID();
|
auto client_pid = ctx.GetPID();
|
||||||
auto result = interface.RegisterProcessHandle(client_pid,
|
auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
|
||||||
process_h->DynamicCast<Kernel::KProcess*>());
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
|
@ -671,12 +670,11 @@ private:
|
||||||
|
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto params = rp.PopRaw<InputParameters>();
|
auto params = rp.PopRaw<InputParameters>();
|
||||||
auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
|
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
|
||||||
|
|
||||||
auto client_pid = ctx.GetPID();
|
auto client_pid = ctx.GetPID();
|
||||||
auto result =
|
auto result = interface.RegisterProcessModuleInfo(
|
||||||
interface.RegisterProcessModuleInfo(client_pid, params.nrr_address, params.nrr_size,
|
client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
|
||||||
process_h->DynamicCast<Kernel::KProcess*>());
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
|
|
|
@ -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
|
// If emulation was shutdown, we are closing service threads, do not write the response back to
|
||||||
// memory that may be shutting down as well.
|
// memory that may be shutting down as well.
|
||||||
if (system.IsPoweredOn()) {
|
if (system.IsPoweredOn()) {
|
||||||
ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
|
ctx.WriteToOutgoingCommandBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/atomic_ops.h"
|
#include "common/atomic_ops.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/heap_tracker.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/page_table.h"
|
#include "common/page_table.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
|
@ -52,10 +53,18 @@ struct Memory::Impl {
|
||||||
} else {
|
} else {
|
||||||
current_page_table->fastmem_arena = nullptr;
|
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,
|
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((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((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
|
||||||
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
|
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
|
||||||
|
@ -64,19 +73,20 @@ struct Memory::Impl {
|
||||||
Common::PageType::Memory);
|
Common::PageType::Memory);
|
||||||
|
|
||||||
if (current_page_table->fastmem_arena) {
|
if (current_page_table->fastmem_arena) {
|
||||||
system.DeviceMemory().buffer.Map(GetInteger(base),
|
buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms,
|
||||||
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((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((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
|
||||||
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
|
MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
|
||||||
Common::PageType::Unmapped);
|
Common::PageType::Unmapped);
|
||||||
|
|
||||||
if (current_page_table->fastmem_arena) {
|
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;
|
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_bytes{};
|
||||||
u64 protect_begin{};
|
u64 protect_begin{};
|
||||||
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
|
||||||
|
@ -102,8 +107,7 @@ struct Memory::Impl {
|
||||||
switch (page_type) {
|
switch (page_type) {
|
||||||
case Common::PageType::RasterizerCachedMemory:
|
case Common::PageType::RasterizerCachedMemory:
|
||||||
if (protect_bytes > 0) {
|
if (protect_bytes > 0) {
|
||||||
system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w,
|
buffer->Protect(protect_begin, protect_bytes, perms);
|
||||||
is_x);
|
|
||||||
protect_bytes = 0;
|
protect_bytes = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -116,7 +120,7 @@ struct Memory::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (protect_bytes > 0) {
|
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) {
|
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.
|
// Iterate over a contiguous CPU address space, marking/unmarking the region.
|
||||||
|
@ -543,9 +549,14 @@ struct Memory::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_page_table->fastmem_arena) {
|
if (current_page_table->fastmem_arena) {
|
||||||
const bool is_read_enable =
|
Common::MemoryPermission perm{};
|
||||||
!Settings::values.use_reactive_flushing.GetValue() || !cached;
|
if (!Settings::values.use_reactive_flushing.GetValue() || !cached) {
|
||||||
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !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
|
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU
|
||||||
|
@ -856,6 +867,13 @@ struct Memory::Impl {
|
||||||
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
|
std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
|
||||||
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
|
||||||
std::mutex sys_core_guard;
|
std::mutex sys_core_guard;
|
||||||
|
|
||||||
|
std::optional<Common::HeapTracker> heap_tracker;
|
||||||
|
#ifdef __linux__
|
||||||
|
Common::HeapTracker* buffer{};
|
||||||
|
#else
|
||||||
|
Common::HostMemory* buffer{};
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory::Memory(Core::System& system_) : system{system_} {
|
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,
|
void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||||
Common::PhysicalAddress target, Common::MemoryPermission perms) {
|
Common::PhysicalAddress target, Common::MemoryPermission perms,
|
||||||
impl->MapMemoryRegion(page_table, base, size, target, 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) {
|
void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
||||||
impl->UnmapRegion(page_table, base, size);
|
bool separate_heap) {
|
||||||
|
impl->UnmapRegion(page_table, base, size, separate_heap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size,
|
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 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(
|
u8* const ptr = impl->GetPointerImpl(
|
||||||
GetInteger(vaddr),
|
GetInteger(vaddr),
|
||||||
[&] {
|
[&] {
|
||||||
|
@ -1056,8 +1078,26 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
||||||
GetInteger(vaddr));
|
GetInteger(vaddr));
|
||||||
mapped = false;
|
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;
|
return mapped && ptr != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Memory::InvalidateSeparateHeap(void* fault_address) {
|
||||||
|
#ifdef __linux__
|
||||||
|
return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Core::Memory
|
} // namespace Core::Memory
|
||||||
|
|
|
@ -86,7 +86,8 @@ public:
|
||||||
* @param perms The permissions to map the memory with.
|
* @param perms The permissions to map the memory with.
|
||||||
*/
|
*/
|
||||||
void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
|
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.
|
* Unmaps a region of the emulated process address space.
|
||||||
|
@ -95,7 +96,8 @@ public:
|
||||||
* @param base The address to begin unmapping at.
|
* @param base The address to begin unmapping at.
|
||||||
* @param size The amount of bytes to unmap.
|
* @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.
|
* Protects a region of the emulated process address space with the new permissions.
|
||||||
|
@ -486,6 +488,7 @@ public:
|
||||||
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
|
||||||
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
|
void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
|
||||||
bool InvalidateNCE(Common::ProcessAddress vaddr, 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);
|
void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -190,15 +190,15 @@ CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
|
||||||
}
|
}
|
||||||
|
|
||||||
CheatEngine::~CheatEngine() {
|
CheatEngine::~CheatEngine() {
|
||||||
core_timing.UnscheduleEvent(event, 0);
|
core_timing.UnscheduleEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheatEngine::Initialize() {
|
void CheatEngine::Initialize() {
|
||||||
event = Core::Timing::CreateEvent(
|
event = Core::Timing::CreateEvent(
|
||||||
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
|
"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<std::chrono::nanoseconds> {
|
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||||
FrameCallback(user_data, ns_late);
|
FrameCallback(ns_late);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
});
|
});
|
||||||
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
|
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
|
||||||
|
@ -239,7 +239,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
|
||||||
|
|
||||||
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
|
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)) {
|
if (is_pending_reload.exchange(false)) {
|
||||||
vm.LoadProgram(cheats);
|
vm.LoadProgram(cheats);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ public:
|
||||||
void Reload(std::vector<CheatEntry> reload_cheats);
|
void Reload(std::vector<CheatEntry> reload_cheats);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
void FrameCallback(std::chrono::nanoseconds ns_late);
|
||||||
|
|
||||||
DmntCheatVm vm;
|
DmntCheatVm vm;
|
||||||
CheatProcessMetadata metadata;
|
CheatProcessMetadata metadata;
|
||||||
|
|
|
@ -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_)
|
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
|
||||||
: core_timing{core_timing_}, memory{memory_} {
|
: core_timing{core_timing_}, memory{memory_} {
|
||||||
event = Core::Timing::CreateEvent(
|
event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
|
||||||
"MemoryFreezer::FrameCallback",
|
[this](s64 time, std::chrono::nanoseconds ns_late)
|
||||||
[this](std::uintptr_t user_data, s64 time,
|
-> std::optional<std::chrono::nanoseconds> {
|
||||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
FrameCallback(ns_late);
|
||||||
FrameCallback(user_data, ns_late);
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
});
|
});
|
||||||
core_timing.ScheduleEvent(memory_freezer_ns, event);
|
core_timing.ScheduleEvent(memory_freezer_ns, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
Freezer::~Freezer() {
|
Freezer::~Freezer() {
|
||||||
core_timing.UnscheduleEvent(event, 0);
|
core_timing.UnscheduleEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Freezer::SetActive(bool is_active) {
|
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; });
|
[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()) {
|
if (!IsActive()) {
|
||||||
LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
|
LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -77,7 +77,7 @@ private:
|
||||||
Entries::iterator FindEntry(VAddr address);
|
Entries::iterator FindEntry(VAddr address);
|
||||||
Entries::const_iterator FindEntry(VAddr address) const;
|
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();
|
void FillEntryReads();
|
||||||
|
|
||||||
std::atomic_bool active{false};
|
std::atomic_bool active{false};
|
||||||
|
|
|
@ -12,6 +12,7 @@ using namespace Common::Literals;
|
||||||
static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
|
static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
|
||||||
static constexpr size_t BACKING_SIZE = 4_GiB;
|
static constexpr size_t BACKING_SIZE = 4_GiB;
|
||||||
static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
|
static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
|
||||||
|
static constexpr auto HEAP = false;
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
|
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
|
||||||
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
|
{ HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
|
||||||
|
@ -20,7 +21,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Simple map", "[common]") {
|
TEST_CASE("HostMemory: Simple map", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
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;
|
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||||
data[0] = 50;
|
data[0] = 50;
|
||||||
|
@ -29,8 +30,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Simple mirror map", "[common]") {
|
TEST_CASE("HostMemory: Simple mirror map", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x5000, 0x3000, 0x2000, PERMS);
|
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
|
||||||
mem.Map(0x8000, 0x4000, 0x1000, PERMS);
|
mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
|
||||||
|
|
||||||
volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
|
volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
|
||||||
volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000;
|
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]") {
|
TEST_CASE("HostMemory: Simple unmap", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
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;
|
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||||
data[75] = 50;
|
data[75] = 50;
|
||||||
REQUIRE(data[75] == 50);
|
REQUIRE(data[75] == 50);
|
||||||
|
|
||||||
mem.Unmap(0x5000, 0x2000);
|
mem.Unmap(0x5000, 0x2000, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
|
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
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;
|
volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
|
||||||
data[0] = 50;
|
data[0] = 50;
|
||||||
REQUIRE(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);
|
REQUIRE(data[0] == 50);
|
||||||
|
|
||||||
mem.Map(0x7000, 0x2000, 0x5000, PERMS);
|
mem.Map(0x7000, 0x2000, 0x5000, PERMS, HEAP);
|
||||||
REQUIRE(data[0x3000] == 50);
|
REQUIRE(data[0x3000] == 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
|
TEST_CASE("HostMemory: Nieche allocation", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x0000, 0, 0x20000, PERMS);
|
mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
|
||||||
mem.Unmap(0x0000, 0x4000);
|
mem.Unmap(0x0000, 0x4000, HEAP);
|
||||||
mem.Map(0x1000, 0, 0x2000, PERMS);
|
mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
|
||||||
mem.Map(0x3000, 0, 0x1000, PERMS);
|
mem.Map(0x3000, 0, 0x1000, PERMS, HEAP);
|
||||||
mem.Map(0, 0, 0x1000, PERMS);
|
mem.Map(0, 0, 0x1000, PERMS, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Full unmap", "[common]") {
|
TEST_CASE("HostMemory: Full unmap", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Unmap(0x8000, 0x4000);
|
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||||
mem.Map(0x6000, 0, 0x16000, PERMS);
|
mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
|
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x0000, 0, 0x4000, PERMS);
|
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Unmap(0x2000, 0x4000);
|
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||||
mem.Map(0x2000, 0x80000, 0x4000, PERMS);
|
mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
|
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Unmap(0x6000, 0x4000);
|
mem.Unmap(0x6000, 0x4000, HEAP);
|
||||||
mem.Map(0x8000, 0, 0x2000, PERMS);
|
mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
|
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x0000, 0, 0x4000, PERMS);
|
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Map(0x4000, 0, 0x1b000, PERMS);
|
mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
|
||||||
mem.Unmap(0x3000, 0x1c000);
|
mem.Unmap(0x3000, 0x1c000, HEAP);
|
||||||
mem.Map(0x3000, 0, 0x20000, PERMS);
|
mem.Map(0x3000, 0, 0x20000, PERMS, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
|
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x0000, 0, 0x4000, PERMS);
|
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Map(0x4000, 0, 0x4000, PERMS);
|
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Unmap(0x2000, 0x4000);
|
mem.Unmap(0x2000, 0x4000, HEAP);
|
||||||
mem.Map(0x2000, 0, 0x4000, PERMS);
|
mem.Map(0x2000, 0, 0x4000, PERMS, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
|
TEST_CASE("HostMemory: Unmap to origin", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x4000, 0, 0x4000, PERMS);
|
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Unmap(0x4000, 0x4000);
|
mem.Unmap(0x4000, 0x4000, HEAP);
|
||||||
mem.Map(0, 0, 0x4000, PERMS);
|
mem.Map(0, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Map(0x4000, 0, 0x4000, PERMS);
|
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Unmap to right", "[common]") {
|
TEST_CASE("HostMemory: Unmap to right", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x4000, 0, 0x4000, PERMS);
|
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||||
mem.Unmap(0x8000, 0x4000);
|
mem.Unmap(0x8000, 0x4000, HEAP);
|
||||||
mem.Map(0x8000, 0, 0x4000, PERMS);
|
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
|
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
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;
|
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||||
ptr[0x1000] = 17;
|
ptr[0x1000] = 17;
|
||||||
|
|
||||||
mem.Unmap(0x6000, 0x2000);
|
mem.Unmap(0x6000, 0x2000, HEAP);
|
||||||
|
|
||||||
REQUIRE(ptr[0x1000] == 17);
|
REQUIRE(ptr[0x1000] == 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
|
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
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;
|
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||||
ptr[0x3000] = 19;
|
ptr[0x3000] = 19;
|
||||||
ptr[0x3fff] = 12;
|
ptr[0x3fff] = 12;
|
||||||
|
|
||||||
mem.Unmap(0x4000, 0x2000);
|
mem.Unmap(0x4000, 0x2000, HEAP);
|
||||||
|
|
||||||
REQUIRE(ptr[0x3000] == 19);
|
REQUIRE(ptr[0x3000] == 19);
|
||||||
REQUIRE(ptr[0x3fff] == 12);
|
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]") {
|
TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
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;
|
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||||
ptr[0x0000] = 19;
|
ptr[0x0000] = 19;
|
||||||
ptr[0x3fff] = 12;
|
ptr[0x3fff] = 12;
|
||||||
|
|
||||||
mem.Unmap(0x1000, 0x2000);
|
mem.Unmap(0x1000, 0x2000, HEAP);
|
||||||
|
|
||||||
REQUIRE(ptr[0x0000] == 19);
|
REQUIRE(ptr[0x0000] == 19);
|
||||||
REQUIRE(ptr[0x3fff] == 12);
|
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]") {
|
TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
|
||||||
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
|
||||||
mem.Map(0x4000, 0x10000, 0x2000, PERMS);
|
mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
|
||||||
mem.Map(0x6000, 0x20000, 0x2000, PERMS);
|
mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
|
||||||
|
|
||||||
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
|
||||||
ptr[0x0000] = 19;
|
ptr[0x0000] = 19;
|
||||||
ptr[0x3fff] = 12;
|
ptr[0x3fff] = 12;
|
||||||
|
|
||||||
mem.Unmap(0x5000, 0x2000);
|
mem.Unmap(0x5000, 0x2000, HEAP);
|
||||||
|
|
||||||
REQUIRE(ptr[0x0000] == 19);
|
REQUIRE(ptr[0x0000] == 19);
|
||||||
REQUIRE(ptr[0x3fff] == 12);
|
REQUIRE(ptr[0x3fff] == 12);
|
||||||
|
|
|
@ -16,20 +16,16 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Numbers are chosen randomly to make sure the correct one is given.
|
// Numbers are chosen randomly to make sure the correct one is given.
|
||||||
constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
|
|
||||||
constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
|
constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
|
||||||
std::array<s64, 5> delays{};
|
std::array<s64, 5> delays{};
|
||||||
|
std::bitset<5> callbacks_ran_flags;
|
||||||
std::bitset<CB_IDS.size()> callbacks_ran_flags;
|
|
||||||
u64 expected_callback = 0;
|
u64 expected_callback = 0;
|
||||||
|
|
||||||
template <unsigned int IDX>
|
template <unsigned int IDX>
|
||||||
std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time,
|
std::optional<std::chrono::nanoseconds> HostCallbackTemplate(s64 time,
|
||||||
std::chrono::nanoseconds ns_late) {
|
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);
|
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();
|
delays[IDX] = ns_late.count();
|
||||||
++expected_callback;
|
++expected_callback;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -76,7 +72,7 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||||
const u64 order = calls_order[i];
|
const u64 order = calls_order[i];
|
||||||
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
|
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
|
||||||
|
|
||||||
core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]);
|
core_timing.ScheduleEvent(future_ns, events[order]);
|
||||||
}
|
}
|
||||||
/// test pause
|
/// test pause
|
||||||
REQUIRE(callbacks_ran_flags.none());
|
REQUIRE(callbacks_ran_flags.none());
|
||||||
|
@ -118,7 +114,7 @@ TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
|
||||||
for (std::size_t i = 0; i < events.size(); i++) {
|
for (std::size_t i = 0; i < events.size(); i++) {
|
||||||
const u64 order = calls_order[i];
|
const u64 order = calls_order[i];
|
||||||
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
|
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(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();
|
const u64 end = core_timing.GetGlobalTimeNs().count();
|
||||||
|
|
|
@ -60,66 +60,72 @@ u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <auto decompress, PixelFormat pixel_format>
|
template <auto decompress, PixelFormat pixel_format>
|
||||||
void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent,
|
void DecompressBlocks(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||||
bool is_signed = false) {
|
bool is_signed = false) {
|
||||||
const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
|
const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
|
||||||
const u32 block_width = std::min(extent.width, BLOCK_SIZE);
|
const u32 block_size = BlockSize(pixel_format);
|
||||||
const u32 block_height = std::min(extent.height, BLOCK_SIZE);
|
const u32 width = copy.image_extent.width;
|
||||||
const u32 pitch = extent.width * out_bpp;
|
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 input_offset = 0;
|
||||||
size_t output_offset = 0;
|
size_t output_offset = 0;
|
||||||
for (u32 slice = 0; slice < extent.depth; ++slice) {
|
for (u32 slice = 0; slice < depth; ++slice) {
|
||||||
for (u32 y = 0; y < extent.height; y += block_height) {
|
for (u32 y = 0; y < height; y += block_height) {
|
||||||
size_t row_offset = 0;
|
size_t src_offset = input_offset;
|
||||||
for (u32 x = 0; x < extent.width;
|
size_t dst_offset = output_offset;
|
||||||
x += block_width, row_offset += block_width * out_bpp) {
|
for (u32 x = 0; x < width; x += block_width) {
|
||||||
const u8* src = input.data() + input_offset;
|
const u8* src = input.data() + src_offset;
|
||||||
u8* const dst = output.data() + output_offset + row_offset;
|
u8* const dst = output.data() + dst_offset;
|
||||||
if constexpr (IsSigned(pixel_format)) {
|
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 {
|
} 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;
|
output_offset += block_height * pitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
|
void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||||
VideoCore::Surface::PixelFormat pixel_format) {
|
VideoCore::Surface::PixelFormat pixel_format) {
|
||||||
switch (pixel_format) {
|
switch (pixel_format) {
|
||||||
case PixelFormat::BC1_RGBA_UNORM:
|
case PixelFormat::BC1_RGBA_UNORM:
|
||||||
case PixelFormat::BC1_RGBA_SRGB:
|
case PixelFormat::BC1_RGBA_SRGB:
|
||||||
DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, extent);
|
DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, copy);
|
||||||
break;
|
break;
|
||||||
case PixelFormat::BC2_UNORM:
|
case PixelFormat::BC2_UNORM:
|
||||||
case PixelFormat::BC2_SRGB:
|
case PixelFormat::BC2_SRGB:
|
||||||
DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, extent);
|
DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, copy);
|
||||||
break;
|
break;
|
||||||
case PixelFormat::BC3_UNORM:
|
case PixelFormat::BC3_UNORM:
|
||||||
case PixelFormat::BC3_SRGB:
|
case PixelFormat::BC3_SRGB:
|
||||||
DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, extent);
|
DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, copy);
|
||||||
break;
|
break;
|
||||||
case PixelFormat::BC4_SNORM:
|
case PixelFormat::BC4_SNORM:
|
||||||
case PixelFormat::BC4_UNORM:
|
case PixelFormat::BC4_UNORM:
|
||||||
DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
|
DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
|
||||||
input, output, extent, pixel_format == PixelFormat::BC4_SNORM);
|
input, output, copy, pixel_format == PixelFormat::BC4_SNORM);
|
||||||
break;
|
break;
|
||||||
case PixelFormat::BC5_SNORM:
|
case PixelFormat::BC5_SNORM:
|
||||||
case PixelFormat::BC5_UNORM:
|
case PixelFormat::BC5_UNORM:
|
||||||
DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
|
DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
|
||||||
input, output, extent, pixel_format == PixelFormat::BC5_SNORM);
|
input, output, copy, pixel_format == PixelFormat::BC5_SNORM);
|
||||||
break;
|
break;
|
||||||
case PixelFormat::BC6H_SFLOAT:
|
case PixelFormat::BC6H_SFLOAT:
|
||||||
case PixelFormat::BC6H_UFLOAT:
|
case PixelFormat::BC6H_UFLOAT:
|
||||||
DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
|
DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
|
||||||
input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT);
|
input, output, copy, pixel_format == PixelFormat::BC6H_SFLOAT);
|
||||||
break;
|
break;
|
||||||
case PixelFormat::BC7_SRGB:
|
case PixelFormat::BC7_SRGB:
|
||||||
case PixelFormat::BC7_UNORM:
|
case PixelFormat::BC7_UNORM:
|
||||||
DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, extent);
|
DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, copy);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
|
LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace VideoCommon {
|
||||||
|
|
||||||
[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);
|
[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);
|
||||||
|
|
||||||
void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent,
|
void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
|
||||||
VideoCore::Surface::PixelFormat pixel_format);
|
VideoCore::Surface::PixelFormat pixel_format);
|
||||||
|
|
||||||
} // namespace VideoCommon
|
} // namespace VideoCommon
|
||||||
|
|
|
@ -837,6 +837,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||||
std::span<u8> output) {
|
std::span<u8> output) {
|
||||||
const size_t guest_size_bytes = input.size_bytes();
|
const size_t guest_size_bytes = input.size_bytes();
|
||||||
const u32 bpp_log2 = BytesPerBlockLog2(info.format);
|
const u32 bpp_log2 = BytesPerBlockLog2(info.format);
|
||||||
|
const Extent2D tile_size = DefaultBlockSize(info.format);
|
||||||
const Extent3D size = info.size;
|
const Extent3D size = info.size;
|
||||||
|
|
||||||
if (info.type == ImageType::Linear) {
|
if (info.type == ImageType::Linear) {
|
||||||
|
@ -847,7 +848,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||||
return {{
|
return {{
|
||||||
.buffer_offset = 0,
|
.buffer_offset = 0,
|
||||||
.buffer_size = guest_size_bytes,
|
.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,
|
.buffer_image_height = size.height,
|
||||||
.image_subresource =
|
.image_subresource =
|
||||||
{
|
{
|
||||||
|
@ -862,7 +863,6 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||||
const LevelInfo level_info = MakeLevelInfo(info);
|
const LevelInfo level_info = MakeLevelInfo(info);
|
||||||
const s32 num_layers = info.resources.layers;
|
const s32 num_layers = info.resources.layers;
|
||||||
const s32 num_levels = info.resources.levels;
|
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 std::array level_sizes = CalculateLevelSizes(level_info, num_levels);
|
||||||
const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing);
|
const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing);
|
||||||
const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels);
|
const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels);
|
||||||
|
@ -926,8 +926,6 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
|
||||||
|
|
||||||
const auto input_offset = input.subspan(copy.buffer_offset);
|
const auto input_offset = input.subspan(copy.buffer_offset);
|
||||||
copy.buffer_offset = output_offset;
|
copy.buffer_offset = output_offset;
|
||||||
copy.buffer_row_length = mip_size.width;
|
|
||||||
copy.buffer_image_height = mip_size.height;
|
|
||||||
|
|
||||||
const auto recompression_setting = Settings::values.astc_recompression.GetValue();
|
const auto recompression_setting = Settings::values.astc_recompression.GetValue();
|
||||||
const bool astc = IsPixelFormatASTC(info.format);
|
const bool astc = IsPixelFormatASTC(info.format);
|
||||||
|
@ -972,16 +970,14 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
|
||||||
bpp_div;
|
bpp_div;
|
||||||
output_offset += static_cast<u32>(copy.buffer_size);
|
output_offset += static_cast<u32>(copy.buffer_size);
|
||||||
} else {
|
} else {
|
||||||
const Extent3D image_extent{
|
DecompressBCn(input_offset, output.subspan(output_offset), copy, info.format);
|
||||||
.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);
|
|
||||||
output_offset += copy.image_extent.width * copy.image_extent.height *
|
output_offset += copy.image_extent.width * copy.image_extent.height *
|
||||||
copy.image_subresource.num_layers *
|
copy.image_subresource.num_layers *
|
||||||
ConvertedBytesPerBlock(info.format);
|
ConvertedBytesPerBlock(info.format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copy.buffer_row_length = mip_size.width;
|
||||||
|
copy.buffer_image_height = mip_size.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -755,7 +755,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
|
||||||
// The wanted format is not supported by hardware, search for alternatives
|
// The wanted format is not supported by hardware, search for alternatives
|
||||||
const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
|
const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
|
||||||
if (alternatives == nullptr) {
|
if (alternatives == nullptr) {
|
||||||
ASSERT_MSG(false,
|
LOG_ERROR(Render_Vulkan,
|
||||||
"Format={} with usage={} and type={} has no defined alternatives and host "
|
"Format={} with usage={} and type={} has no defined alternatives and host "
|
||||||
"hardware does not support it",
|
"hardware does not support it",
|
||||||
wanted_format, wanted_usage, format_type);
|
wanted_format, wanted_usage, format_type);
|
||||||
|
@ -774,7 +774,7 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
// No alternatives found, panic
|
// No alternatives found, panic
|
||||||
ASSERT_MSG(false,
|
LOG_ERROR(Render_Vulkan,
|
||||||
"Format={} with usage={} and type={} is not supported by the host hardware and "
|
"Format={} with usage={} and type={} is not supported by the host hardware and "
|
||||||
"doesn't support any of the alternatives",
|
"doesn't support any of the alternatives",
|
||||||
wanted_format, wanted_usage, format_type);
|
wanted_format, wanted_usage, format_type);
|
||||||
|
|
|
@ -246,8 +246,10 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec
|
||||||
.objectHandle = reinterpret_cast<u64>(handle),
|
.objectHandle = reinterpret_cast<u64>(handle),
|
||||||
.pObjectName = name,
|
.pObjectName = name,
|
||||||
};
|
};
|
||||||
|
if (dld->vkSetDebugUtilsObjectNameEXT) {
|
||||||
Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info));
|
Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
|
|
@ -168,14 +168,6 @@ class GMainWindow : public QMainWindow {
|
||||||
/// Max number of recently loaded items to keep track of
|
/// Max number of recently loaded items to keep track of
|
||||||
static const int max_recent_files_item = 10;
|
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 {
|
enum {
|
||||||
CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
|
CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
|
||||||
CREATE_SHORTCUT_MSGBOX_SUCCESS,
|
CREATE_SHORTCUT_MSGBOX_SUCCESS,
|
||||||
|
|
Loading…
Reference in a new issue