diff --git a/.github/workflows/dreamcast.yml b/.github/workflows/dreamcast.yml index 3c929007ed4..ec2e133825f 100644 --- a/.github/workflows/dreamcast.yml +++ b/.github/workflows/dreamcast.yml @@ -25,13 +25,23 @@ concurrency: jobs: build: runs-on: ubuntu-latest - container: azihassan/kallistios:docker + container: azihassan/kallistios:8c7fbfcf3c38c4da82bc067e1719b74c2f93f755 steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Patch KOS for streaming a large quantity of files + run: | + source /opt/toolchains/dc/kos/environ.sh && \ + cd /opt/toolchains/dc/kos && \ + sed -i 's/THD_KERNEL_STACK_SIZE (64 \* 1024)/THD_KERNEL_STACK_SIZE (256 \* 1024)/g' /opt/toolchains/dc/kos/kernel/arch/dreamcast/include/arch/arch.h && \ + echo "THD_KERNEL_STACK_SIZE changed to $(cat /opt/toolchains/dc/kos/kernel/arch/dreamcast/include/arch/arch.h | grep THD_KERNEL_STACK_SIZE)" && \ + sed -i 's/THD_STACK_SIZE 32768/THD_STACK_SIZE (128*1024)/g' /opt/toolchains/dc/kos/kernel/arch/dreamcast/include/arch/arch.h && \ + echo "THD_STACK_SIZE changed to $(cat /opt/toolchains/dc/kos/kernel/arch/dreamcast/include/arch/arch.h | grep THD_STACK_SIZE)" && \ + make clean && make CFLAGS+="-DFS_CD_MAX_FILES=4096 -DFD_SETSIZE=4096" + - name: Build unpack_and_minify_mpq run: | git clone https://github.com/diasurgical/devilutionx-mpq-tools/ && \ @@ -82,7 +92,7 @@ jobs: - name: Build DevilutionX run: | - source /opt/toolchains/dc/kos/environ.sh && cd build && kos-make + source /opt/toolchains/dc/kos/environ.sh && cd build && kos-make -j4 - name: Generate .cdi run: | diff --git a/3rdParty/libfmt/CMakeLists.txt b/3rdParty/libfmt/CMakeLists.txt index 48936347d4e..6ff1525615a 100644 --- a/3rdParty/libfmt/CMakeLists.txt +++ b/3rdParty/libfmt/CMakeLists.txt @@ -34,6 +34,6 @@ if(TARGET_PLATFORM STREQUAL "rg99" OR PLATFORM_DREAMCAST) endif() # https://github.com/fmtlib/fmt/issues/4189 -if(NINTENDO_3DS OR NINTENDO_SWITCH OR VITA) +if(NINTENDO_3DS OR NINTENDO_SWITCH OR VITA OR PLATFORM_DREAMCAST) target_compile_definitions(fmt PUBLIC FMT_USE_FALLBACK_FILE=1) endif() diff --git a/CMake/platforms/dreamcast.cmake b/CMake/platforms/dreamcast.cmake index 3161c23f131..e151bdde968 100644 --- a/CMake/platforms/dreamcast.cmake +++ b/CMake/platforms/dreamcast.cmake @@ -10,7 +10,7 @@ set(DEFAULT_WIDTH 640) set(DEFAULT_HEIGHT 480) set(DEVILUTIONX_GAMEPAD_TYPE Nintendo) -set(NOSOUND ON) +#set(NOSOUND ON) set(DEVILUTIONX_STATIC_ZLIB ON) set(UNPACKED_MPQS ON) set(UNPACKED_SAVES ON) @@ -21,7 +21,7 @@ set(DEVILUTIONX_DISABLE_STRIP ON) set(DEVILUTIONX_ASSETS_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/data/") set(BUILD_ASSETS_MPQ OFF) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/threads-stub") +#list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/threads-stub") list(APPEND DEVILUTIONX_PLATFORM_COMPILE_DEFINITIONS __DREAMCAST__) add_compile_options(-fpermissive) @@ -47,4 +47,13 @@ set(JOY_BUTTON_START 3) set(SDL_INCLUDE_DIR /usr/include/SDL/) set(SDL_LIBRARY /usr/lib/libSDL.a) -add_compile_options(-flto=none) +add_compile_options(-flto=auto) + +# Must stream most of the audio due to RAM constraints. +set(STREAM_ALL_AUDIO_MIN_FILE_SIZE 1023) + +# Must use a smaller audio buffer due to RAM constraints. +set(DEFAULT_AUDIO_BUFFER_SIZE 768) + +# Use lower resampling quality for FPS. +set(DEFAULT_AUDIO_RESAMPLING_QUALITY 1) diff --git a/Dockerfile b/Dockerfile index 6c327a9fa6f..65dbc859d0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,14 @@ -FROM azihassan/kallistios:fdffe33635239d46bcccf0d5c4d59bb7d2d91f38 +FROM azihassan/kallistios:8c7fbfcf3c38c4da82bc067e1719b74c2f93f755 + +RUN echo "Patching KOS for streaming a large quantity of files" +RUN source /opt/toolchains/dc/kos/environ.sh && \ + cd /opt/toolchains/dc/kos && \ + sed -i 's/THD_KERNEL_STACK_SIZE (64 \* 1024)/THD_KERNEL_STACK_SIZE (256 \* 1024)/g' /opt/toolchains/dc/kos/kernel/arch/dreamcast/include/arch/arch.h && \ + echo "THD_KERNEL_STACK_SIZE changed to $(cat /opt/toolchains/dc/kos/kernel/arch/dreamcast/include/arch/arch.h | grep THD_KERNEL_STACK_SIZE)" && \ + sed -i 's/THD_STACK_SIZE 32768/THD_STACK_SIZE (128*1024)/g' /opt/toolchains/dc/kos/kernel/arch/dreamcast/include/arch/arch.h && \ + echo "THD_STACK_SIZE changed to $(cat /opt/toolchains/dc/kos/kernel/arch/dreamcast/include/arch/arch.h | grep THD_STACK_SIZE)" && \ + make clean && \ + make CFLAGS+="-DFS_CD_MAX_FILES=4096 -DFD_SETSIZE=4096" RUN echo "Building unpack_and_minify_mpq..." RUN git clone https://github.com/diasurgical/devilutionx-mpq-tools/ && \ diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index f291841044c..804a38b9c43 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -281,6 +281,9 @@ if(DEVILUTIONX_SCREENSHOT_FORMAT STREQUAL DEVILUTIONX_SCREENSHOT_FORMAT_PNG) utils/surface_to_png.cpp ) endif() +if(PLATFORM_DREAMCAST) + list(APPEND libdevilutionx_SRCS memory_stats.cpp) +endif() add_devilutionx_library(libdevilutionx OBJECT ${libdevilutionx_SRCS}) target_include_directories(libdevilutionx PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/Source/diablo.cpp b/Source/diablo.cpp index d8974ce86fc..183c94967ad 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -106,6 +106,10 @@ #include #endif +#ifdef __DREAMCAST__ +#include "memory_stats.h" +#endif + namespace devilution { uint32_t DungeonSeeds[NUMLEVELS]; @@ -2543,6 +2547,9 @@ void setOnInitialized(void (*callback)()) int DiabloMain(int argc, char **argv) { +#ifdef __DREAMCAST__ + set_system_ram(); +#endif #ifdef _DEBUG SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG); #endif diff --git a/Source/effects.cpp b/Source/effects.cpp index 1df897a0916..8807da12b8c 100644 --- a/Source/effects.cpp +++ b/Source/effects.cpp @@ -261,8 +261,16 @@ void effects_cleanup_sfx() void sound_init() { +#ifdef __DREAMCAST__ + bool isDreamcast = true; +#else + bool isDreamcast = false; +#endif uint8_t mask = sfx_MISC; - if (gbIsMultiplayer) { + // only load the SFX of the current character on the dreamcast + // because it still doesn't have network multiplayer mode + // so we only need the sfx of the player's class + if (gbIsMultiplayer && !isDreamcast) { mask |= sfx_WARRIOR; if (!gbIsSpawn) mask |= (sfx_ROGUE | sfx_SORCERER); diff --git a/Source/engine/sound.cpp b/Source/engine/sound.cpp index b9125881849..0609d44cd7b 100644 --- a/Source/engine/sound.cpp +++ b/Source/engine/sound.cpp @@ -24,6 +24,11 @@ #include "utils/str_cat.hpp" #include "utils/stubs.h" +// todo remove this +// changing this value in dreamcast.cmake causes the whole project to recompile +// redefined here to only recompile sound.cpp +#define STREAM_ALL_AUDIO_MIN_FILE_SIZE 10 * 1024 + namespace devilution { bool gbSndInited; @@ -71,6 +76,11 @@ bool LoadAudioFile(const char *path, bool stream, bool errorDialog, SoundSample #endif #endif +#ifdef __DREAMCAST__ + Log(">AUDIO: Loading audio file {} with streaming {} ({} kilobytes)", foundPath, stream, ref.size() / 1024.0); + print_ram_stats(); + Log("\n\n\n"); +#endif if (stream) { if (result.SetChunkStream(foundPath, isMp3, /*logErrors=*/true) != 0) { if (errorDialog) { @@ -95,6 +105,7 @@ bool LoadAudioFile(const char *path, bool stream, bool errorDialog, SoundSample return false; } const int error = result.SetChunk(waveFile, size, isMp3); + if (error != 0) { if (errorDialog) ErrSdl(); @@ -204,6 +215,9 @@ TSnd::~TSnd() void snd_init() { +#ifdef __DREAMCAST__ + ::snd_init(); +#endif sgOptions.Audio.soundVolume.SetValue(CapVolume(*sgOptions.Audio.soundVolume)); gbSoundOn = *sgOptions.Audio.soundVolume > VOLUME_MIN; sgbSaveSoundOn = gbSoundOn; @@ -228,6 +242,7 @@ void snd_init() void snd_deinit() { if (gbSndInited) { + snd_shutdown(); Aulib::quit(); duplicateSoundsMutex = std::nullopt; } diff --git a/Source/engine/sound.h b/Source/engine/sound.h index 6ca09631499..ebd0945cf09 100644 --- a/Source/engine/sound.h +++ b/Source/engine/sound.h @@ -13,6 +13,11 @@ #include "utils/attributes.h" #ifndef NOSOUND +#ifdef __DREAMCAST__ +#include "memory_stats.h" +#include +#endif + #include "utils/soundsample.h" #endif diff --git a/Source/memory_stats.cpp b/Source/memory_stats.cpp new file mode 100644 index 00000000000..e8dd8ce7923 --- /dev/null +++ b/Source/memory_stats.cpp @@ -0,0 +1,65 @@ +// DREAMCAST memory stats related code + +#include "memory_stats.h" + +#include +#include + +#include +#include + +static unsigned long systemRam = 0x00000000; +static unsigned long elfOffset = 0x00000000; +static unsigned long stackSize = 0x00000000; + +extern unsigned long end; +extern unsigned long start; + +#define _end end +#define _start start +void print_VRAM_stats() +{ + pvr_mem_available(); +} + +void set_system_ram() +{ + systemRam = 0x8d000000 - 0x8c000000; + elfOffset = 0x8c000000; + + stackSize = (int)&_end - (int)&_start + ((int)&_start - elfOffset); +} + +unsigned long get_system_ram() +{ + return systemRam; +} + +unsigned long get_free_ram() +{ + struct mallinfo mi = mallinfo(); + return systemRam - (mi.usmblks + stackSize); +} + +void print_ram_stats() +{ + float sys_ram, free_ram, used_ram, pvr_ram, sound_ram; + sys_ram = (float)get_system_ram() / (float)(1024 * 1024); + free_ram = (float)get_free_ram() / (float)(1024 * 1024); + used_ram = (sys_ram - free_ram); + // pvr_ram = (float)pvr_mem_available() / (float)(1024*1024); + sound_ram = (float)snd_mem_available() / (float)(1024 * 1024); + + printf("\n---------\nRAM stats (MB):\nTotal: %.2f, Free: %.2f, Used: %.2f, PVR: %.2f, Sound: %.2f\n---------\n", sys_ram, free_ram, used_ram, pvr_ram, sound_ram); + // printf("\n---------\nRAM stats (MB):\nTotal: %.2f, Free: %.2f, Used: %.2f\n---------\n", sys_ram, free_ram, used_ram); +} + +void get_ram_stats(float *sys_ram, float *free_ram, float *used_ram, float *pvr_ram) +{ + *sys_ram = (float)get_system_ram() / (float)(1024 * 1024); + *free_ram = (float)get_free_ram() / (float)(1024 * 1024); + *used_ram = (*sys_ram - *free_ram); + if (pvr_ram) { + //*pvr_ram = (float)pvr_mem_available() / (float)(1024*1024); + } +} diff --git a/Source/memory_stats.h b/Source/memory_stats.h new file mode 100644 index 00000000000..895007b70fa --- /dev/null +++ b/Source/memory_stats.h @@ -0,0 +1,11 @@ +#ifndef __MEMORY_STATS_H__ +#define __MEMORY_STATS_H__ 1 + +void set_system_ram(); +void print_VRAM_stats(); +unsigned long get_system_ram(); +unsigned long get_free_ram(); +void print_ram_stats(); +void get_ram_stats(float *sys_ram, float *free_ram, float *used_ram, float *pvr_ram); + +#endif