From e2fbc0663f47dc4db52c8482d71a833da6ac762b Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 3 Aug 2022 18:19:11 -0400 Subject: [PATCH] Update submodules; changes for emscripten support --- CMakeLists.txt | 3 ++ Runtime/CDvdFile.cpp | 60 ++++++++++++++++++++++++++++++++++++- Runtime/CDvdFile.hpp | 7 +++++ Runtime/CMain.cpp | 7 +++++ Runtime/CMakeLists.txt | 5 +++- Runtime/ImGuiConsole.cpp | 8 ++++- Runtime/MP1/CGBASupport.cpp | 30 +++++++++++++++++++ extern/CMakeLists.txt | 4 +-- extern/athena | 2 +- extern/aurora | 2 +- extern/boo | 2 +- extern/kabufuda | 2 +- extern/zeus | 2 +- imgui/CMakeLists.txt | 2 +- imgui/ImGuiEngine.cpp | 1 + version.h.in | 2 ++ 16 files changed, 128 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5a88e031..c3496bbf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,9 @@ if (APPLE AND NOT TVOS AND CMAKE_SYSTEM_NAME STREQUAL tvOS) set(TVOS ON) set(IOS OFF) endif () +if (EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX .html) +endif () string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" ATHENA_ARCH) string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" ATHENA_HOST_ARCH) diff --git a/Runtime/CDvdFile.cpp b/Runtime/CDvdFile.cpp index eff4d5449..297d8fb0c 100644 --- a/Runtime/CDvdFile.cpp +++ b/Runtime/CDvdFile.cpp @@ -19,25 +19,50 @@ class CFileDvdRequest : public IDvdRequest { u32 m_len; ESeekOrigin m_whence; int m_offset; + +#ifdef HAS_DVD_THREAD std::atomic_bool m_cancel = {false}; std::atomic_bool m_complete = {false}; +#else + bool m_cancel = false; + bool m_complete = false; +#endif std::function m_callback; public: ~CFileDvdRequest() override { CFileDvdRequest::PostCancelRequest(); } void WaitUntilComplete() override { +#ifdef HAS_DVD_THREAD while (!m_complete.load() && !m_cancel.load()) { std::unique_lock lk{CDvdFile::m_WaitMutex}; } +#else + if (!m_complete && !m_cancel) { + CDvdFile::DoWork(); + } +#endif + } + bool IsComplete() override { +#ifdef HAS_DVD_THREAD + return m_complete.load(); +#else + if (!m_complete) { + CDvdFile::DoWork(); + } + return m_complete; +#endif } - bool IsComplete() override { return m_complete.load(); } void PostCancelRequest() override { +#ifdef HAS_DVD_THREAD if (m_complete.load() || m_cancel.load()) { return; } std::unique_lock waitlk{CDvdFile::m_WaitMutex}; m_cancel.store(true); +#else + m_cancel = true; +#endif } [[nodiscard]] EMediaType GetMediaType() const override { return EMediaType::File; } @@ -53,9 +78,15 @@ public: , m_callback(std::move(cb)) {} void DoRequest() { +#ifdef HAS_DVD_THREAD if (m_cancel.load()) { return; } +#else + if (m_cancel) { + return; + } +#endif u32 readLen = 0; if (m_whence == ESeekOrigin::Cur && m_offset == 0) { readLen = m_reader->read(m_buf, m_len); @@ -84,15 +115,21 @@ public: if (m_callback) { m_callback(readLen); } +#ifdef HAS_DVD_THREAD m_complete.store(true); +#else + m_complete = true; +#endif } }; +#ifdef HAS_DVD_THREAD std::thread CDvdFile::m_WorkerThread; std::mutex CDvdFile::m_WorkerMutex; std::condition_variable CDvdFile::m_WorkerCV; std::mutex CDvdFile::m_WaitMutex; std::atomic_bool CDvdFile::m_WorkerRun = {false}; +#endif std::vector> CDvdFile::m_RequestQueue; std::string CDvdFile::m_rootDirectory; std::unique_ptr CDvdFile::m_dolBuf; @@ -106,7 +143,17 @@ CDvdFile::CDvdFile(std::string_view path) : x18_path(path) { } } +// single-threaded hack +void CDvdFile::DoWork() { + for (std::shared_ptr& req : m_RequestQueue) { + auto& concreteReq = static_cast(*req); + concreteReq.DoRequest(); + } + m_RequestQueue.clear(); +} + void CDvdFile::WorkerProc() { +#ifdef HAS_DVD_THREAD logvisor::RegisterThreadName("CDvdFile"); OPTICK_THREAD("CDvdFile"); @@ -130,15 +177,20 @@ void CDvdFile::WorkerProc() { } m_WorkerCV.wait(lk); } +#endif } std::shared_ptr CDvdFile::AsyncSeekRead(void* buf, u32 len, ESeekOrigin whence, int off, std::function&& cb) { std::shared_ptr ret = std::make_shared(*this, buf, len, whence, off, std::move(cb)); +#ifdef HAS_DVD_THREAD std::unique_lock lk{m_WorkerMutex}; +#endif m_RequestQueue.emplace_back(ret); +#ifdef HAS_DVD_THREAD lk.unlock(); m_WorkerCV.notify_one(); +#endif return ret; } @@ -205,20 +257,25 @@ nod::Node* CDvdFile::ResolvePath(std::string_view path) { } bool CDvdFile::Initialize(const std::string_view& path) { +#ifdef HAS_DVD_THREAD if (m_WorkerRun.load()) { return true; } +#endif m_DvdRoot = nod::OpenDiscFromImage(path); if (!m_DvdRoot) { return false; } m_dolBuf = m_DvdRoot->getDataPartition()->getDOLBuf(); +#ifdef HAS_DVD_THREAD m_WorkerRun.store(true); m_WorkerThread = std::thread(WorkerProc); +#endif return true; } void CDvdFile::Shutdown() { +#ifdef HAS_DVD_THREAD if (!m_WorkerRun.load()) { return; } @@ -227,6 +284,7 @@ void CDvdFile::Shutdown() { if (m_WorkerThread.joinable()) { m_WorkerThread.join(); } +#endif m_RequestQueue.clear(); } diff --git a/Runtime/CDvdFile.hpp b/Runtime/CDvdFile.hpp index d220fd765..607c8ed65 100644 --- a/Runtime/CDvdFile.hpp +++ b/Runtime/CDvdFile.hpp @@ -15,6 +15,10 @@ #include #include +#ifndef EMSCRIPTEN +#define HAS_DVD_THREAD +#endif + namespace metaforce { enum class ESeekOrigin { Begin = 0, Cur = 1, End = 2 }; @@ -33,11 +37,13 @@ class CDvdFile { friend class CFileDvdRequest; static std::unique_ptr m_DvdRoot; // static std::unordered_map m_caseInsensitiveMap; +#ifdef HAS_DVD_THREAD static std::thread m_WorkerThread; static std::mutex m_WorkerMutex; static std::condition_variable m_WorkerCV; static std::mutex m_WaitMutex; static std::atomic_bool m_WorkerRun; +#endif static std::vector> m_RequestQueue; static std::string m_rootDirectory; static std::unique_ptr m_dolBuf; @@ -57,6 +63,7 @@ public: static void SetRootDirectory(const std::string_view& rootDir); static void Shutdown(); static u8* GetDolBuf() { return m_dolBuf.get(); } + static void DoWork(); CDvdFile(std::string_view path); operator bool() const { return m_reader.operator bool(); } diff --git a/Runtime/CMain.cpp b/Runtime/CMain.cpp index caa645ccc..82166abd7 100644 --- a/Runtime/CMain.cpp +++ b/Runtime/CMain.cpp @@ -367,6 +367,9 @@ public: if (m_voiceEngine) { m_voiceEngine->pumpAndMixVoices(); } +#ifdef EMSCRIPTEN + CDvdFile::DoWork(); +#endif CGraphics::TickRenderTimings(); ++logvisor::FrameIndex; } @@ -433,12 +436,16 @@ static void SetupBasics() { } static bool IsClientLoggingEnabled(int argc, char** argv) { +#ifdef EMSCRIPTEN + return true; +#else for (int i = 1; i < argc; ++i) { if (!strncmp(argv[i], "-l", 2)) { return true; } } return false; +#endif } static void SetupLogging() {} diff --git a/Runtime/CMakeLists.txt b/Runtime/CMakeLists.txt index 4014a6905..dc941081d 100644 --- a/Runtime/CMakeLists.txt +++ b/Runtime/CMakeLists.txt @@ -154,7 +154,7 @@ if (NOT GEKKO AND NOT NX AND NOT IOS AND NOT TVOS) set(DISCORD_RPC_LIBRARY "discord-rpc") endif() set(RUNTIME_LIBRARIES amuse zeus nod NESEmulator libjpeg-turbo jbus kabufuda logvisor OptickCore - imgui_support aurora::aurora SDL2::SDL2-static SDL2::SDL2main + imgui_support aurora::aurora boo # TODO move audiodev ${DISCORD_RPC_LIBRARY} ${ZLIB_LIBRARIES} @@ -278,3 +278,6 @@ if (WINDOWS_STORE) # This should match the Package.appxmanifest set_property(TARGET metaforce PROPERTY VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION "10.0.14393.0") endif () +if (EMSCRIPTEN) + target_link_options(metaforce PRIVATE -sTOTAL_MEMORY=268435456 -sALLOW_MEMORY_GROWTH --preload-file "${CMAKE_SOURCE_DIR}/files@/") +endif () diff --git a/Runtime/ImGuiConsole.cpp b/Runtime/ImGuiConsole.cpp index 140ec88a5..6c9cc5712 100644 --- a/Runtime/ImGuiConsole.cpp +++ b/Runtime/ImGuiConsole.cpp @@ -700,11 +700,17 @@ void ImGuiConsole::ShowAboutWindow(bool preLaunch) { } } #endif +#ifdef EMSCRIPTEN + if (ImGuiButtonCenter("Load Game")) { + m_gameDiscSelected = "game.iso"; + } +#else if (!m_lastDiscPath.empty()) { if (ImGuiButtonCenter("Load Previous Game")) { m_gameDiscSelected = m_lastDiscPath; } } +#endif ImGui::Dummy(padding); } if (m_errorString) { @@ -828,7 +834,7 @@ void ImGuiConsole::ShowDebugOverlay() { } hasPrevious = true; - ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {}\n"), metaforce::CGraphics::GetFPS())); + ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {:.1f}\n"), io.Framerate)); } if (m_inGameTime && g_GameState != nullptr) { if (hasPrevious) { diff --git a/Runtime/MP1/CGBASupport.cpp b/Runtime/MP1/CGBASupport.cpp index 044508788..0410809f1 100644 --- a/Runtime/MP1/CGBASupport.cpp +++ b/Runtime/MP1/CGBASupport.cpp @@ -5,20 +5,31 @@ #include "Runtime/CBasics.hpp" #include "Runtime/CDvdRequest.hpp" +#ifndef EMSCRIPTEN +#define ENABLE_JBUS +#endif + +#ifdef ENABLE_JBUS #include #include +#endif namespace metaforce::MP1 { +#ifdef ENABLE_JBUS static jbus::Listener g_JbusListener; static std::unique_ptr g_JbusEndpoint; +#endif void CGBASupport::Initialize() { +#ifdef ENABLE_JBUS jbus::Initialize(); g_JbusListener.start(); +#endif } void CGBASupport::GlobalPoll() { +#ifdef ENABLE_JBUS if (g_JbusEndpoint && !g_JbusEndpoint->connected()) g_JbusEndpoint.reset(); if (!g_JbusEndpoint) { @@ -26,6 +37,7 @@ void CGBASupport::GlobalPoll() { if (g_JbusEndpoint) g_JbusEndpoint->setChan(3); } +#endif } CGBASupport::CGBASupport() : CDvdFile("client_pad.bin") { @@ -56,6 +68,7 @@ u8 CGBASupport::CalculateFusionJBusChecksum(const u8* data, size_t len) { } bool CGBASupport::PollResponse() { +#ifdef ENABLE_JBUS if (!g_JbusEndpoint) return false; @@ -127,10 +140,13 @@ bool CGBASupport::PollResponse() { if (x44_fusionLinked && (bytes[2] & 0x1) != 0) x45_fusionBeat = true; +#endif return true; } +#ifdef ENABLE_JBUS static void JoyBootDone(jbus::ThreadLocalEndpoint& endpoint, jbus::EJoyReturn status) {} +#endif void CGBASupport::Update(float dt) { switch (x34_phase) { @@ -144,6 +160,7 @@ void CGBASupport::Update(float dt) { [[fallthrough]]; case EPhase::PollProbe: +#ifdef ENABLE_JBUS /* SIProbe poll normally occurs here with 4 second timeout */ if (!g_JbusEndpoint) { x34_phase = EPhase::Failed; @@ -151,17 +168,23 @@ void CGBASupport::Update(float dt) { } x40_siChan = g_JbusEndpoint->getChan(); x34_phase = EPhase::StartJoyBusBoot; +#endif [[fallthrough]]; case EPhase::StartJoyBusBoot: +#ifdef ENABLE_JBUS x34_phase = EPhase::PollJoyBusBoot; if (!g_JbusEndpoint || g_JbusEndpoint->GBAJoyBootAsync(x40_siChan * 2, 2, x2c_buffer.get(), x28_fileSize, &x3c_status, JoyBootDone) != jbus::GBA_READY) { x34_phase = EPhase::Failed; } +#else + x34_phase = EPhase::Failed; +#endif break; case EPhase::PollJoyBusBoot: +#ifdef ENABLE_JBUS u8 percent; if (g_JbusEndpoint && g_JbusEndpoint->GBAGetProcessStatus(percent) == jbus::GBA_BUSY) break; @@ -171,9 +194,13 @@ void CGBASupport::Update(float dt) { } x38_timeout = 4.f; x34_phase = EPhase::DataTransfer; +#else + x34_phase = EPhase::Failed; +#endif break; case EPhase::DataTransfer: +#ifdef ENABLE_JBUS if (PollResponse()) { x34_phase = EPhase::Complete; break; @@ -181,6 +208,9 @@ void CGBASupport::Update(float dt) { x38_timeout = std::max(0.f, x38_timeout - dt); if (x38_timeout == 0.f) x34_phase = EPhase::Failed; +#else + x34_phase = EPhase::Failed; +#endif break; default: diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 8a4d2cffb..919d8feb7 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -1,5 +1,5 @@ option(BUILD_ATHENA "Build Athena libraries from source" ON) -if (WIN32 OR APPLE) +if (CMAKE_HOST_SYSTEM_NAME STREQUAL Windows OR CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) # Default to binary atdna on Windows & macOS option(BUILD_ATDNA "Build atdna utility from source (requires LLVM)" OFF) else () @@ -8,7 +8,7 @@ endif () if (NOT BUILD_ATDNA) set(ATHENA_BASE_URL "https://github.com/libAthena/athena/releases/download/latest") - if (WIN32) + if (CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) set(ATHENA_EXT 7z) else () set(ATHENA_EXT tar.gz) diff --git a/extern/athena b/extern/athena index c6046d915..fa346ace4 160000 --- a/extern/athena +++ b/extern/athena @@ -1 +1 @@ -Subproject commit c6046d91507d2a9dca46e29564acc0421322f7e9 +Subproject commit fa346ace4774e4ac66644be6d11908a80f0151b6 diff --git a/extern/aurora b/extern/aurora index 04590f30c..893cabe55 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 04590f30cd90814d70fa7d1fe4a5f1e814b177c2 +Subproject commit 893cabe55a31a6f59c77854d503d891025204acb diff --git a/extern/boo b/extern/boo index 6e4030212..39eb95a82 160000 --- a/extern/boo +++ b/extern/boo @@ -1 +1 @@ -Subproject commit 6e4030212c1a2a21a79a6d69660b6e7a52cf2885 +Subproject commit 39eb95a82440e1e614533b702e1cda814e01f69c diff --git a/extern/kabufuda b/extern/kabufuda index 32090b4d4..4f3fbc0d0 160000 --- a/extern/kabufuda +++ b/extern/kabufuda @@ -1 +1 @@ -Subproject commit 32090b4d4ab70dbfa07f1f2994964f983ab43a69 +Subproject commit 4f3fbc0d0df2ee90a760b2d2e2bb8e2126bd92cb diff --git a/extern/zeus b/extern/zeus index ac7d83009..e9ec10a38 160000 --- a/extern/zeus +++ b/extern/zeus @@ -1 +1 @@ -Subproject commit ac7d83009dae97a28ba779d00188e3d76e9b9821 +Subproject commit e9ec10a3823a3c734e70c99b4aa50ae2a7e7e81f diff --git a/imgui/CMakeLists.txt b/imgui/CMakeLists.txt index 6fcf65d68..e6db195ac 100644 --- a/imgui/CMakeLists.txt +++ b/imgui/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(imgui_support NotoMono.cpp MetaforceIcon.cpp ) -target_link_libraries(imgui_support PUBLIC aurora::aurora imgui) +target_link_libraries(imgui_support PUBLIC aurora::aurora imgui ${ZLIB_LIBRARIES}) target_include_directories(imgui_support PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(imgui_support PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/imgui/ImGuiEngine.cpp b/imgui/ImGuiEngine.cpp index 624b1a089..f5452dae9 100644 --- a/imgui/ImGuiEngine.cpp +++ b/imgui/ImGuiEngine.cpp @@ -1,6 +1,7 @@ #include "ImGuiEngine.hpp" #include +#include #include "Runtime/Streams/CMemoryInStream.hpp" #include "Runtime/Streams/CZipInputStream.hpp" diff --git a/version.h.in b/version.h.in index b4b9c9d57..1d349cca2 100644 --- a/version.h.in +++ b/version.h.in @@ -13,6 +13,8 @@ #define METAFORCE_DLPACKAGE "metaforce-@METAFORCE_WC_DESCRIBE@-@PLATFORM_NAME@-x86_64-@METAFORCE_VECTOR_ISA@" #elif defined(__aarch64__) || defined(_M_ARM64) #define METAFORCE_DLPACKAGE "metaforce-@METAFORCE_WC_DESCRIBE@-@PLATFORM_NAME@-arm64" +#elif defined(EMSCRIPTEN) +#define METAFORCE_DLPACKAGE "metaforce-@METAFORCE_WC_DESCRIBE@-@PLATFORM_NAME@-wasm" #endif #endif