2022-04-23 03:59:50 -05:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2020-04-08 16:35:07 -05:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2020-11-17 18:58:41 -06:00
|
|
|
#include <utility>
|
2020-04-08 16:35:07 -05:00
|
|
|
|
|
|
|
namespace Common {
|
|
|
|
|
2020-11-17 18:58:41 -06:00
|
|
|
void* AllocateMemoryPages(std::size_t size) noexcept;
|
|
|
|
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
2020-04-08 16:35:07 -05:00
|
|
|
|
|
|
|
template <typename T>
|
2020-11-17 18:58:41 -06:00
|
|
|
class VirtualBuffer final {
|
2020-04-08 16:35:07 -05:00
|
|
|
public:
|
core/memory: Read and write page table atomically
Squash attributes into the pointer's integer, making them an uintptr_t
pair containing 2 bits at the bottom and then the pointer. These bits
are currently unused thanks to alignment requirements.
Configure Dynarmic to mask out these bits on pointer reads.
While we are at it, remove some unused attributes carried over from
Citra.
Read/Write and other hot functions use a two step unpacking process that
is less readable to stop MSVC from emitting an extra AND instruction in
the hot path:
mov rdi,rcx
shr rdx,0Ch
mov r8,qword ptr [rax+8]
mov rax,qword ptr [r8+rdx*8]
mov rdx,rax
-and al,3
and rdx,0FFFFFFFFFFFFFFFCh
je Core::Memory::Memory::Impl::Read<unsigned char>
mov rax,qword ptr [vaddr]
movzx eax,byte ptr [rdx+rax]
2020-12-29 18:16:57 -06:00
|
|
|
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
|
|
|
|
// using std::atomic_ref once libc++ has support for it
|
|
|
|
// static_assert(
|
|
|
|
// std::is_trivially_constructible_v<T>,
|
|
|
|
// "T must be trivially constructible, as non-trivial constructors will not be executed "
|
|
|
|
// "with the current allocator");
|
2020-11-17 19:09:55 -06:00
|
|
|
|
2020-04-08 16:35:07 -05:00
|
|
|
constexpr VirtualBuffer() = default;
|
|
|
|
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
|
|
|
|
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
|
|
|
}
|
|
|
|
|
2020-11-17 18:58:41 -06:00
|
|
|
~VirtualBuffer() noexcept {
|
2020-04-08 16:35:07 -05:00
|
|
|
FreeMemoryPages(base_ptr, alloc_size);
|
|
|
|
}
|
|
|
|
|
2020-11-17 18:58:41 -06:00
|
|
|
VirtualBuffer(const VirtualBuffer&) = delete;
|
|
|
|
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
|
|
|
|
|
|
|
|
VirtualBuffer(VirtualBuffer&& other) noexcept
|
|
|
|
: alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
|
|
|
|
nullptr} {}
|
|
|
|
|
|
|
|
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
|
|
|
|
alloc_size = std::exchange(other.alloc_size, 0);
|
|
|
|
base_ptr = std::exchange(other.base_ptr, nullptr);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2020-04-08 16:35:07 -05:00
|
|
|
void resize(std::size_t count) {
|
2020-11-19 06:54:00 -06:00
|
|
|
const auto new_size = count * sizeof(T);
|
|
|
|
if (new_size == alloc_size) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-08 16:35:07 -05:00
|
|
|
FreeMemoryPages(base_ptr, alloc_size);
|
|
|
|
|
2020-11-19 06:54:00 -06:00
|
|
|
alloc_size = new_size;
|
2020-04-08 16:35:07 -05:00
|
|
|
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
|
|
|
}
|
|
|
|
|
2020-08-14 08:38:45 -05:00
|
|
|
[[nodiscard]] constexpr const T& operator[](std::size_t index) const {
|
2020-04-08 16:35:07 -05:00
|
|
|
return base_ptr[index];
|
|
|
|
}
|
|
|
|
|
2020-08-14 08:38:45 -05:00
|
|
|
[[nodiscard]] constexpr T& operator[](std::size_t index) {
|
2020-04-08 16:35:07 -05:00
|
|
|
return base_ptr[index];
|
|
|
|
}
|
|
|
|
|
2020-08-14 08:38:45 -05:00
|
|
|
[[nodiscard]] constexpr T* data() {
|
2020-04-08 16:35:07 -05:00
|
|
|
return base_ptr;
|
|
|
|
}
|
|
|
|
|
2020-08-14 08:38:45 -05:00
|
|
|
[[nodiscard]] constexpr const T* data() const {
|
2020-04-08 16:35:07 -05:00
|
|
|
return base_ptr;
|
|
|
|
}
|
|
|
|
|
2020-08-14 08:38:45 -05:00
|
|
|
[[nodiscard]] constexpr std::size_t size() const {
|
2020-04-08 16:35:07 -05:00
|
|
|
return alloc_size / sizeof(T);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::size_t alloc_size{};
|
|
|
|
T* base_ptr{};
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace Common
|