Skip to content

Commit

Permalink
Initial fastmem support
Browse files Browse the repository at this point in the history
* PCSX2 fastmem depression

* Move away from PCSX2 fastmem

* Add enum_flag_ops.hpp

* Finally building on Windows

* Almost got a PoC

* Fix arm64 builds

* This somehow works

* This also works...

* Properly fix fastmem

* Add free region manager

* Update boost

* Add ScopeExit

* Comment out asserts on Linux/Mac/Android

* Comment out ASSERT_MSG asserts too

* Fix derp

* Attempt to fix Android

* Disable fastmem on Android

* Fix Android again maybe pt 2

* android pls

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* Update host_memory.cpp

* Properly reset memory arena on reset

* Proper ashmem code for Android

* more

* Add temporary Android buildjet script for faster prototype builds

* Fix fastmem (again)

* Clean up shared memory
  • Loading branch information
wheremyfoodat authored Dec 3, 2024
1 parent 28461a1 commit c088911
Show file tree
Hide file tree
Showing 18 changed files with 2,139 additions and 13 deletions.
74 changes: 74 additions & 0 deletions .github/workflows/Android_Build_Buildjet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Android Build (Buildjet)

on:
push:
branches:
- master
pull_request:

jobs:
arm64:
runs-on: buildjet-32vcpu-ubuntu-2204

strategy:
matrix:
build_type:
- release

steps:
- name: Set BUILD_TYPE variable
run: echo "BUILD_TYPE=${{ matrix.build_type }}" >> $GITHUB_ENV

- uses: actions/checkout@v4
- name: Fetch submodules
run: git submodule update --init --recursive

- name: Set up gradle caches
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-pandroid-arm64-${{ github.sha }}
restore-keys: |
${{ runner.os }}-pandroid-arm64-
- name: Setup Vulkan SDK
uses: humbletim/[email protected]
with:
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'

- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_CXX_FLAGS="-march=armv8-a+crypto"

- name: Build
run: |
# Apply patch for GLES compatibility
git apply ./.github/gles.patch
# Build the project with CMake
cmake --build ${{github.workspace}}/build --config RelWithDebInfo
# Strip the generated library and move it to the appropriate location
${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --strip-unneeded ./build/libAlber.so
mv ./build/libAlber.so ./src/pandroid/app/src/main/jniLibs/arm64-v8a/
# Build the Android app with Gradle
cd src/pandroid
./gradlew assembleDebug
ls -R app/build/outputs
cd ../..
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: Android APKs (arm64)
path: |
./src/pandroid/app/build/outputs/apk/${{ env.BUILD_TYPE }}/app-${{ env.BUILD_TYPE }}.apk
14 changes: 12 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 @@ -280,7 +282,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 @@ -362,7 +364,8 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.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/enum_flag_ops.hpp include/services/dsp_firmware_db.hpp include/frontend_settings.hpp
include/dynamic_library.hpp
)

cmrc_add_resource_library(
Expand All @@ -380,6 +383,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 @@ -625,6 +631,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
Loading

0 comments on commit c088911

Please sign in to comment.