Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial fastmem support #662

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ option(BUILD_HYDRA_CORE "Build a Hydra core" OFF)
option(BUILD_LIBRETRO_CORE "Build a Libretro core" OFF)
option(ENABLE_RENDERDOC_API "Build with support for Renderdoc's capture API for graphics debugging" ON)
option(DISABLE_SSE4 "Build with SSE4 instructions disabled, may reduce performance" OFF)
option(ENABLE_FASTMEM "Build with support for hardware fastmem" ON)

set(OPENGL_PROFILE ${DEFAULT_OPENGL_PROFILE} CACHE STRING "OpenGL profile to use if OpenGL is enabled. Valid values are 'OpenGL' and 'OpenGLES'.")
set_property(CACHE OPENGL_PROFILE PROPERTY STRINGS OpenGL OpenGLES)
Expand Down Expand Up @@ -165,6 +166,7 @@ include_directories(third_party/toml11)
include_directories(third_party/glm)
include_directories(third_party/renderdoc)
include_directories(third_party/duckstation)
include_directories(third_party/host_memory/include)

add_subdirectory(third_party/cmrc)

Expand Down Expand Up @@ -305,7 +307,7 @@ set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/miniaudio.cpp src/renderdoc.cpp
src/frontend_settings.cpp
src/frontend_settings.cpp src/dynamic_library.cpp
)
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
Expand Down Expand Up @@ -389,7 +391,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/enum_flag_ops.hpp include/dynamic_library.hpp
)

cmrc_add_resource_library(
Expand All @@ -407,6 +409,9 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp

third_party/cityhash/cityhash.cpp
third_party/xxhash/xxhash.c

third_party/host_memory/host_memory.cpp
third_party/host_memory/virtual_buffer.cpp
)

if(ENABLE_LUAJIT AND NOT ANDROID)
Expand Down Expand Up @@ -652,6 +657,10 @@ if(ENABLE_HTTP_SERVER)
target_compile_definitions(AlberCore PRIVATE PANDA3DS_ENABLE_HTTP_SERVER=1)
endif()

if(ENABLE_FASTMEM)
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HARDWARE_FASTMEM=1)
endif()

# Configure frontend

if(ENABLE_QT_GUI)
Expand Down
73 changes: 73 additions & 0 deletions include/dynamic_library.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: 2019 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <string>

namespace Common {
/**
* Provides a platform-independent interface for loading a dynamic library and retrieving symbols.
* The interface maintains an internal reference count to allow one handle to be shared between
* multiple users.
*/
class DynamicLibrary final {
public:
/// Default constructor, does not load a library.
explicit DynamicLibrary();

/// Automatically loads the specified library. Call IsOpen() to check validity before use.
explicit DynamicLibrary(const char* filename);

/// Initializes the dynamic library with an already opened handle.
explicit DynamicLibrary(void* handle_);

/// Moves the library.
DynamicLibrary(DynamicLibrary&&) noexcept;
DynamicLibrary& operator=(DynamicLibrary&&) noexcept;

/// Delete copies, we can't copy a dynamic library.
DynamicLibrary(const DynamicLibrary&) = delete;
DynamicLibrary& operator=(const DynamicLibrary&) = delete;

/// Closes the library.
~DynamicLibrary();

/// Returns the specified library name with the platform-specific suffix added.
[[nodiscard]] static std::string getUnprefixedFilename(const char* filename);

/// Returns the specified library name in platform-specific format.
/// Major/minor versions will not be included if set to -1.
/// If libname already contains the "lib" prefix, it will not be added again.
/// Windows: LIBNAME-MAJOR-MINOR.dll
/// Linux: libLIBNAME.so.MAJOR.MINOR
/// Mac: libLIBNAME.MAJOR.MINOR.dylib
[[nodiscard]] static std::string getVersionedFilename(const char* libname, int major = -1, int minor = -1);

/// Returns true if a module is loaded, otherwise false.
[[nodiscard]] bool isOpen() const { return handle != nullptr; }

/// Loads (or replaces) the handle with the specified library file name.
/// Returns true if the library was loaded and can be used.
[[nodiscard]] bool open(const char* filename);

/// Unloads the library, any function pointers from this library are no longer valid.
void close();

/// Returns the address of the specified symbol (function or variable) as an untyped pointer.
/// If the specified symbol does not exist in this library, nullptr is returned.
[[nodiscard]] void* getSymbolAddress(const char* name) const;

/// Obtains the address of the specified symbol, automatically casting to the correct type.
/// Returns true if the symbol was found and assigned, otherwise false.
template <typename T>
[[nodiscard]] bool getSymbol(const char* name, T* ptr) const {
*ptr = reinterpret_cast<T>(getSymbolAddress(name));
return *ptr != nullptr;
}

private:
/// Platform-dependent data type representing a dynamic library handle.
void* handle = nullptr;
};
} // namespace Common
4 changes: 2 additions & 2 deletions include/emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ enum class ROMType {

class Emulator {
EmulatorConfig config;
Memory memory;
CPU cpu;
GPU gpu;
Memory memory;
Kernel kernel;
std::unique_ptr<Audio::DSPCore> dsp;
Scheduler scheduler;
Expand All @@ -55,7 +55,7 @@ class Emulator {
static constexpr u32 width = 400;
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
ROMType romType = ROMType::None;
bool running = false; // Is the emulator running a game?
bool running = false; // Is the emulator running a game?

private:
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
Expand Down
60 changes: 60 additions & 0 deletions include/enum_flag_ops.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <type_traits>

#define DECLARE_ENUM_FLAG_OPERATORS(type) \
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
} \
[[nodiscard]] constexpr type operator&(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
} \
[[nodiscard]] constexpr type operator^(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \
} \
[[nodiscard]] constexpr type operator<<(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) << static_cast<T>(b)); \
} \
[[nodiscard]] constexpr type operator>>(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) >> static_cast<T>(b)); \
} \
constexpr type& operator|=(type& a, type b) noexcept { \
a = a | b; \
return a; \
} \
constexpr type& operator&=(type& a, type b) noexcept { \
a = a & b; \
return a; \
} \
constexpr type& operator^=(type& a, type b) noexcept { \
a = a ^ b; \
return a; \
} \
constexpr type& operator<<=(type& a, type b) noexcept { \
a = a << b; \
return a; \
} \
constexpr type& operator>>=(type& a, type b) noexcept { \
a = a >> b; \
return a; \
} \
[[nodiscard]] constexpr type operator~(type key) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<type>(~static_cast<T>(key)); \
} \
[[nodiscard]] constexpr bool True(type key) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<T>(key) != 0; \
} \
[[nodiscard]] constexpr bool False(type key) noexcept { \
using T = std::underlying_type_t<type>; \
return static_cast<T>(key) == 0; \
}
42 changes: 39 additions & 3 deletions include/memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
#include <bitset>
#include <filesystem>
#include <fstream>
#include <memory>
#include <optional>
#include <vector>

#include "config.hpp"
#include "crypto/aes_engine.hpp"
#include "handles.hpp"
#include "helpers.hpp"
#include "loader/ncsd.hpp"
#include "host_memory/host_memory.h"
#include "loader/3dsx.hpp"
#include "loader/ncsd.hpp"
#include "services/region_codes.hpp"

namespace PhysicalAddrs {
Expand Down Expand Up @@ -108,7 +110,7 @@ class Memory {
u8* dspRam; // Provided to us by Audio
u8* vram; // Provided to the memory class by the GPU class

u64& cpuTicks; // Reference to the CPU tick counter
const u64* cpuTicks = nullptr; // Pointer to the CPU tick counter, provided to us by the CPU class
using SharedMemoryBlock = KernelMemoryTypes::SharedMemoryBlock;

// Our dynarmic core uses page tables for reads and writes with 4096 byte pages
Expand Down Expand Up @@ -141,6 +143,36 @@ class Memory {
static constexpr u32 DSP_DATA_MEMORY_OFFSET = u32(256_KB);

private:
// We also use MMU-accelerated fastmem for fast memory emulation
// This means that we've got a 4GB memory arena which is organized the same way as the emulated 3DS' memory map
// And we can access this directly instead of calling the memory read/write functions, which would be slower
// Regions that are not mapped or can't be accelerated this way will segfault, and the caller (eg dynarmic), will
// handle this segfault and call the Slower memory read/write functions
bool useFastmem = false;
static constexpr size_t FASTMEM_FCRAM_OFFSET = 0; // Offset of FCRAM in the fastmem arena
static constexpr size_t FASTMEM_DSP_RAM_OFFSET = FASTMEM_FCRAM_OFFSET + FCRAM_SIZE; // Offset of DSP RAM

static constexpr size_t FASTMEM_BACKING_SIZE = FCRAM_SIZE + DSP_RAM_SIZE;
// Total size of the virtual address space we will occupy (4GB)
static constexpr size_t FASTMEM_VIRTUAL_SIZE = 4_GB;

Common::HostMemory* arena;

void addFastmemView(u32 guestVaddr, size_t arenaOffset, size_t size, bool w, bool x = false) {
if (useFastmem) {
Common::MemoryPermission perms = Common::MemoryPermission::Read;
if (w) {
perms |= Common::MemoryPermission::Write;
}

if (x) {
//perms |= Common::MemoryPermission::Execute;
}

arena->Map(guestVaddr, arenaOffset, size, perms, false);
}
}

std::bitset<FCRAM_PAGE_COUNT> usedFCRAMPages;
std::optional<u32> findPaddr(u32 size);
u64 timeSince3DSEpoch();
Expand Down Expand Up @@ -172,7 +204,7 @@ class Memory {
u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore)
u32 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore)

Memory(u64& cpuTicks, const EmulatorConfig& config);
Memory(const EmulatorConfig& config);
void reset();
void* getReadPointer(u32 address);
void* getWritePointer(u32 address);
Expand Down Expand Up @@ -295,8 +327,12 @@ class Memory {

void setVRAM(u8* pointer) { vram = pointer; }
void setDSPMem(u8* pointer) { dspRam = pointer; }
void setCPUTicks(const u64& ticks) { cpuTicks = &ticks; }

bool allocateMainThreadStack(u32 size);
Regions getConsoleRegion();
void copySharedFont(u8* ptr, u32 vaddr);

bool isFastmemEnabled() { return useFastmem; }
u8* getFastmemArenaBase() { return arena->VirtualBasePointer(); }
};
7 changes: 7 additions & 0 deletions src/core/CPU/cpu_dynarmic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

CPU::CPU(Memory& mem, Kernel& kernel, Emulator& emu) : mem(mem), emu(emu), scheduler(emu.getScheduler()), env(mem, kernel, emu.getScheduler()) {
cp15 = std::make_shared<CP15>();
mem.setCPUTicks(getTicksRef());

Dynarmic::A32::UserConfig config;
config.arch_version = Dynarmic::A32::ArchVersion::v6K;
Expand All @@ -15,6 +16,12 @@ CPU::CPU(Memory& mem, Kernel& kernel, Emulator& emu) : mem(mem), emu(emu), sched
config.global_monitor = &exclusiveMonitor;
config.processor_id = 0;

if (mem.isFastmemEnabled()) {
config.fastmem_pointer = u64(mem.getFastmemArenaBase());
} else {
config.fastmem_pointer = std::nullopt;
}

jit = std::make_unique<Dynarmic::A32::Jit>(config);
}

Expand Down
28 changes: 24 additions & 4 deletions src/core/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ CMRC_DECLARE(ConsoleFonts);

using namespace KernelMemoryTypes;

Memory::Memory(u64& cpuTicks, const EmulatorConfig& config) : cpuTicks(cpuTicks), config(config) {
fcram = new uint8_t[FCRAM_SIZE]();
Memory::Memory(const EmulatorConfig& config) : config(config) {
arena = new Common::HostMemory(FASTMEM_BACKING_SIZE, FASTMEM_VIRTUAL_SIZE, false);

readTable.resize(totalPageCount, 0);
writeTable.resize(totalPageCount, 0);
memoryInfo.reserve(32); // Pre-allocate some room for memory allocation info to avoid dynamic allocs

fcram = arena->BackingBasePointer() + FASTMEM_FCRAM_OFFSET;
// arenaDSPRam = arena->BackingBasePointer() + FASTMEM_DSP_RAM_OFFSET;

useFastmem = arena->VirtualBasePointer() != nullptr;
}

void Memory::reset() {
Expand All @@ -29,6 +34,11 @@ void Memory::reset() {
usedUserMemory = u32(0_MB);
usedSystemMemory = u32(0_MB);

if (useFastmem) {
// Unmap any mappings when resetting
arena->Unmap(0, 4_GB, false);
}

for (u32 i = 0; i < totalPageCount; i++) {
readTable[i] = 0;
writeTable[i] = 0;
Expand Down Expand Up @@ -70,6 +80,9 @@ void Memory::reset() {
writeTable[i + initialPage] = pointer;
}

// Allocate RW mapping for DSP RAM
// addFastmemView(VirtualAddrs::DSPMemStart, FASTMEM_DSP_RAM_OFFSET, DSP_RAM_SIZE, true, false);

// Later adjusted based on ROM header when possible
region = Regions::USA;
}
Expand Down Expand Up @@ -163,8 +176,8 @@ u32 Memory::read32(u32 vaddr) {
case ConfigMem::Datetime0 + 4:
return u32(timeSince3DSEpoch() >> 32); // top 32 bits
// Ticks since time was last updated. For now we return the current tick count
case ConfigMem::Datetime0 + 8: return u32(cpuTicks);
case ConfigMem::Datetime0 + 12: return u32(cpuTicks >> 32);
case ConfigMem::Datetime0 + 8: return u32(*cpuTicks);
case ConfigMem::Datetime0 + 12: return u32(*cpuTicks >> 32);
case ConfigMem::Datetime0 + 16: return 0xFFB0FF0; // Unknown, set by PTM
case ConfigMem::Datetime0 + 20:
case ConfigMem::Datetime0 + 24:
Expand Down Expand Up @@ -357,6 +370,9 @@ std::optional<u32> Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool l

// Mark FCRAM page as allocated and go on
usedFCRAMPages[physPage] = true;
// Add mapping to the fastmem arena
addFastmemView(size_t(virtualPage) * pageSize, FASTMEM_FCRAM_OFFSET + size_t(physPage) * pageSize, pageSize, w, false);

virtualPage++;
physPage++;
}
Expand Down Expand Up @@ -481,6 +497,10 @@ void Memory::mirrorMapping(u32 destAddress, u32 sourceAddress, u32 size) {

const u32 pageCount = size / pageSize; // How many pages we need to mirror
for (u32 i = 0; i < pageCount; i++) {
if (useFastmem) {
Helpers::panic("Unimplemented: Mirror mapping with fastmem enabled");
}

// Redo the shift here to "properly" handle wrapping around the address space instead of reading OoB
const u32 sourcePage = sourceAddress / pageSize;
const u32 destPage = destAddress / pageSize;
Expand Down
Loading
Loading