diff --git a/.gitignore b/.gitignore index 3665593..c231a70 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ saves/ .vscode/ .zed/ out/ +.qtcreator/ *.toml *.ini *.z64 diff --git a/CMakeLists.txt b/CMakeLists.txt index 90600d6..bb9b88f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,6 @@ else() message(FATAL_ERROR "Git not found, please define KAIZEN_GIT_COMMIT_HASH manually.") endif() endif() - configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/version.hpp.in ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp) include_directories( @@ -152,7 +151,7 @@ set(CAPSTONE_MIPS_SUPPORT ON) set(CAPSTONE_X86_SUPPORT ON) add_subdirectory(external/capstone) -qt_add_executable(kaizen +qt_add_executable(kaizen src/frontend/main.cpp src/frontend/KaizenGui.hpp src/frontend/KaizenGui.cpp @@ -171,7 +170,6 @@ qt_add_executable(kaizen src/utils/Options.cpp src/utils/File.cpp) - if (WIN32) set(MIO_LIB mio::mio_full_winapi) else() @@ -179,10 +177,6 @@ else() endif() target_link_libraries(kaizen PUBLIC Qt6::Core Qt6::Gui Qt6::Widgets SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend) -target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED) -set_target_properties(kaizen PROPERTIES - WIN32_EXECUTABLE ON - MACOSX_BUNDLE ON) if (SANITIZERS) message("UBSAN AND ASAN: ON") diff --git a/LICENSE b/LICENSE index 492872b..e937c73 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2023 Simone Coco +Copyright 2026 Iris Coco Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/external/parallel-rdp/ParallelRDPWrapper.cpp b/external/parallel-rdp/ParallelRDPWrapper.cpp index 5aad206..46aab61 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.cpp +++ b/external/parallel-rdp/ParallelRDPWrapper.cpp @@ -4,7 +4,6 @@ #include #include #include -#include using namespace Vulkan; using namespace RDP; @@ -23,7 +22,8 @@ Program *fullscreen_quad_program; // Copied and modified from WSI::init_context_from_platform Util::IntrusivePtr InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices, - const Context::SystemHandles &system_handles) { + const Context::SystemHandles &system_handles, + Vulkan::InstanceFactory *instanceFactory) { VK_ASSERT(platform); const auto instance_ext = platform->get_instance_extensions(); const auto device_ext = platform->get_device_extensions(); @@ -32,6 +32,9 @@ Util::IntrusivePtr InitVulkanContext(WSIPlatform *platform, const unsig new_context->set_application_info(platform->get_application_info()); new_context->set_num_thread_indices(num_thread_indices); new_context->set_system_handles(system_handles); + if (instanceFactory) { + new_context->set_instance_factory(instanceFactory); + } if (!new_context->init_instance(instance_ext.data(), instance_ext.size(), CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) { @@ -55,14 +58,15 @@ Util::IntrusivePtr InitVulkanContext(WSIPlatform *platform, const unsig } void ParallelRDP::LoadWSIPlatform(const std::shared_ptr &wsi_platform, - const std::shared_ptr &newWindowInfo) { + const std::shared_ptr &newWindowInfo, + Vulkan::InstanceFactory *instanceFactory) { wsi = std::make_shared(); wsi->set_backbuffer_srgb(false); wsi->set_platform(wsi_platform.get()); wsi->set_present_mode(PresentMode::SyncToVBlank); if (constexpr Context::SystemHandles handles; - !wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) { + !wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles, instanceFactory))) { panic("Failed to initialize WSI: init_from_existing_context() failed"); } @@ -78,8 +82,9 @@ void ParallelRDP::LoadWSIPlatform(const std::shared_ptr &wsi_platfo } void ParallelRDP::Init(const std::shared_ptr &wsiPlatform, - const std::shared_ptr &newWindowInfo, const u8 *rdram) { - LoadWSIPlatform(wsiPlatform, newWindowInfo); + const std::shared_ptr &newWindowInfo, Vulkan::InstanceFactory *instanceFactory, + const u8 *rdram) { + LoadWSIPlatform(wsiPlatform, newWindowInfo, instanceFactory); ResourceLayout vertLayout; ResourceLayout fragLayout; diff --git a/external/parallel-rdp/ParallelRDPWrapper.hpp b/external/parallel-rdp/ParallelRDPWrapper.hpp index fc47daa..6389aab 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.hpp +++ b/external/parallel-rdp/ParallelRDPWrapper.hpp @@ -15,7 +15,8 @@ class ParallelRDP { virtual ~WindowInfo() = default; }; - void Init(const std::shared_ptr &, const std::shared_ptr &, const u8 *); + void Init(const std::shared_ptr &, const std::shared_ptr &, + Vulkan::InstanceFactory *, const u8 *); template void UpdateScreen() const; @@ -29,7 +30,8 @@ class ParallelRDP { std::shared_ptr windowInfo; private: - void LoadWSIPlatform(const std::shared_ptr &, const std::shared_ptr &); + void LoadWSIPlatform(const std::shared_ptr &, const std::shared_ptr &, + Vulkan::InstanceFactory *); void DrawFullscreenTexturedQuad(Util::IntrusivePtr, Util::IntrusivePtr) const; void UpdateScreen(Util::IntrusivePtr) const; }; diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index e316d4a..090def5 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -5,7 +5,7 @@ namespace n64 { Core::Core() : interpreter(*mem, regs) { - const auto selectedCpu = Options::GetInstance().GetValue("cpu", "type"); + const auto selectedCpu = Options::GetCpuType(); if (selectedCpu == "interpreter") { cpuType = PlainInterpreter; } else if (selectedCpu == "cached_interpreter") { @@ -88,7 +88,7 @@ void Core::StepRSP(const u32 cpuCycles) { } } -void Core::Run(const float volumeL, const float volumeR) { +void Core::Run() { MMIO &mmio = mem->mmio; bool broken = false; @@ -116,7 +116,7 @@ void Core::Run(const float volumeL, const float volumeR) { mmio.mi.InterruptRaise(MI::Interrupt::VI); } - mmio.ai.Step(frameCycles, volumeL, volumeR); + mmio.ai.Step(frameCycles); Scheduler::GetInstance().Tick(frameCycles); } } diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 709f999..7e67360 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -32,7 +32,7 @@ struct Core { void Reset(); void LoadROM(const std::string &); void LoadTAS(const fs::path &) const; - void Run(float volumeL, float volumeR); + void Run(); void TogglePause() { pause = !pause; } inline void ToggleBreakpoint(s64 addr) { if (breakpoints.contains(addr)) { diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index 63a0529..7391202 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -31,7 +31,7 @@ void Mem::Reset() { void Mem::LoadSRAM(SaveType save_type, fs::path path) { if (save_type == SAVE_SRAM_256k) { std::error_code err; - std::string savePath = Options::GetInstance().GetValue("general", "savePath"); + std::string savePath = Options::GetSavesPath(); if (!savePath.empty()) { path = savePath / path.filename(); } diff --git a/src/backend/core/mem/Flash.cpp b/src/backend/core/mem/Flash.cpp index 0882676..a84c998 100644 --- a/src/backend/core/mem/Flash.cpp +++ b/src/backend/core/mem/Flash.cpp @@ -8,190 +8,190 @@ constexpr auto FLASH_SIZE = 1_mb; Flash::Flash(mio::mmap_sink &saveData) : saveData(saveData) {} void Flash::Reset() { - state = FlashState::Idle; - writeOffs = {}; - state = {}; - status = {}; - eraseOffs = {}; - writeBuf = {}; + state = FlashState::Idle; + writeOffs = {}; + state = {}; + status = {}; + eraseOffs = {}; + writeBuf = {}; } void Flash::Load(SaveType saveType, const std::string &path) { - if (saveType == SAVE_FLASH_1m) { - fs::path flashPath_ = path; - std::string savePath = Options::GetInstance().GetValue("general", "savePath"); - if (!savePath.empty()) { - flashPath_ = savePath / flashPath_.filename(); - } - flashPath = flashPath_.replace_extension(".flash").string(); - std::error_code error; - if (saveData.is_mapped()) { - saveData.sync(error); - if (error) { - panic("Could not sync {}", flashPath); - } - saveData.unmap(); - } + if (saveType == SAVE_FLASH_1m) { + fs::path flashPath_ = path; + std::string savePath = Options::GetSavesPath(); + if (!savePath.empty()) { + flashPath_ = savePath / flashPath_.filename(); + } + flashPath = flashPath_.replace_extension(".flash").string(); + std::error_code error; + if (saveData.is_mapped()) { + saveData.sync(error); + if (error) { + panic("Could not sync {}", flashPath); + } + saveData.unmap(); + } - auto flashVec = ircolib::ReadFileBinary(flashPath); - if (flashVec.empty()) { - std::vector dummy{}; - dummy.resize(FLASH_SIZE); - ircolib::WriteFileBinary(dummy, flashPath); - flashVec = ircolib::ReadFileBinary(flashPath); - } + auto flashVec = ircolib::ReadFileBinary(flashPath); + if (flashVec.empty()) { + std::vector dummy{}; + dummy.resize(FLASH_SIZE); + ircolib::WriteFileBinary(dummy, flashPath); + flashVec = ircolib::ReadFileBinary(flashPath); + } - if (flashVec.size() != FLASH_SIZE) { - panic("Corrupt SRAM!"); - } + if (flashVec.size() != FLASH_SIZE) { + panic("Corrupt SRAM!"); + } - saveData = mio::make_mmap_sink(flashPath, error); - if (error) { - panic("Could not make mmap {}", flashPath); + saveData = mio::make_mmap_sink(flashPath, error); + if (error) { + panic("Could not make mmap {}", flashPath); + } } - } } void Flash::CommandExecute() const { - trace("Flash::CommandExecute"); - switch (state) { - case FlashState::Idle: - break; - case FlashState::Erase: - if (saveData.is_mapped()) { - for (int i = 0; i < 128; i++) { - saveData[eraseOffs + i] = 0xFF; - } - } else { - panic("Accessing flash when not mapped!"); + trace("Flash::CommandExecute"); + switch (state) { + case FlashState::Idle: + break; + case FlashState::Erase: + if (saveData.is_mapped()) { + for (int i = 0; i < 128; i++) { + saveData[eraseOffs + i] = 0xFF; + } + } else { + panic("Accessing flash when not mapped!"); + } + break; + case FlashState::Write: + if (saveData.is_mapped()) { + for (int i = 0; i < 128; i++) { + saveData[writeOffs + i] = writeBuf[i]; + } + } else { + panic("Accessing flash when not mapped!"); + } + break; + case FlashState::Read: + panic("Execute command when flash in read state"); + break; + case FlashState::Status: + break; } - break; - case FlashState::Write: - if (saveData.is_mapped()) { - for (int i = 0; i < 128; i++) { - saveData[writeOffs + i] = writeBuf[i]; - } - } else { - panic("Accessing flash when not mapped!"); - } - break; - case FlashState::Read: - panic("Execute command when flash in read state"); - break; - case FlashState::Status: - break; - } } void Flash::CommandStatus() { - state = FlashState::Status; - status = 0x1111800100C20000; + state = FlashState::Status; + status = 0x1111800100C20000; } void Flash::CommandSetEraseOffs(u32 val) { eraseOffs = (val & 0xffff) << 7; } void Flash::CommandErase() { - state = FlashState::Erase; - status = 0x1111800800C20000LL; + state = FlashState::Erase; + status = 0x1111800800C20000LL; } void Flash::CommandSetWriteOffs(u32 val) { - writeOffs = (val & 0xffff) << 7; - status = 0x1111800400C20000LL; + writeOffs = (val & 0xffff) << 7; + status = 0x1111800400C20000LL; } void Flash::CommandWrite() { state = FlashState::Write; } void Flash::CommandRead() { - state = FlashState::Read; - status = 0x11118004F0000000; + state = FlashState::Read; + status = 0x11118004F0000000; } template <> void Flash::Write(u32 index, u32 val) { - if (index > 0) { - u8 cmd = val >> 24; - switch (cmd) { - case FLASH_COMMAND_EXECUTE: - CommandExecute(); - break; - case FLASH_COMMAND_STATUS: - CommandStatus(); - break; - case FLASH_COMMAND_SET_ERASE_OFFSET: - CommandSetEraseOffs(val); - break; - case FLASH_COMMAND_ERASE: - CommandErase(); - break; - case FLASH_COMMAND_SET_WRITE_OFFSET: - CommandSetWriteOffs(val); - break; - case FLASH_COMMAND_WRITE: - CommandWrite(); - break; - case FLASH_COMMAND_READ: - CommandRead(); - break; - default: - warn("Invalid flash command: {:02X}", cmd); + if (index > 0) { + u8 cmd = val >> 24; + switch (cmd) { + case FLASH_COMMAND_EXECUTE: + CommandExecute(); + break; + case FLASH_COMMAND_STATUS: + CommandStatus(); + break; + case FLASH_COMMAND_SET_ERASE_OFFSET: + CommandSetEraseOffs(val); + break; + case FLASH_COMMAND_ERASE: + CommandErase(); + break; + case FLASH_COMMAND_SET_WRITE_OFFSET: + CommandSetWriteOffs(val); + break; + case FLASH_COMMAND_WRITE: + CommandWrite(); + break; + case FLASH_COMMAND_READ: + CommandRead(); + break; + default: + warn("Invalid flash command: {:02X}", cmd); + } + } else { + warn("Flash Write of {:08X} @ {:08X}", val, index); } - } else { - warn("Flash Write of {:08X} @ {:08X}", val, index); - } } template <> void Flash::Write(u32 index, u8 val) { - switch (state) { - case FlashState::Idle: - panic("Invalid FlashState::Idle with Write"); - case FlashState::Status: - panic("Invalid FlashState::Status with Write"); - case FlashState::Erase: - panic("Invalid FlashState::Erase with Write"); - case FlashState::Read: - panic("Invalid FlashState::Read with Write"); - case FlashState::Write: - assert(index <= 0x7F && "Out of range flash Write8"); - writeBuf[index] = val; - break; - default: - warn("Invalid flash state on Write: {:02X}", static_cast(state)); - } + switch (state) { + case FlashState::Idle: + panic("Invalid FlashState::Idle with Write"); + case FlashState::Status: + panic("Invalid FlashState::Status with Write"); + case FlashState::Erase: + panic("Invalid FlashState::Erase with Write"); + case FlashState::Read: + panic("Invalid FlashState::Read with Write"); + case FlashState::Write: + assert(index <= 0x7F && "Out of range flash Write8"); + writeBuf[index] = val; + break; + default: + warn("Invalid flash state on Write: {:02X}", static_cast(state)); + } } template <> u8 Flash::Read(const u32 index) const { - switch (state) { - case FlashState::Idle: - panic("Flash read byte while in state FLASH_STATE_IDLE"); - case FlashState::Write: - panic("Flash read byte while in state FLASH_STATE_WRITE"); - case FlashState::Read: - if (saveData.is_mapped()) { - const u8 value = saveData[index]; - trace("Flash read byte in state read: index {:08X} = {:02X}", index, value); - return value; - } + switch (state) { + case FlashState::Idle: + panic("Flash read byte while in state FLASH_STATE_IDLE"); + case FlashState::Write: + panic("Flash read byte while in state FLASH_STATE_WRITE"); + case FlashState::Read: + if (saveData.is_mapped()) { + const u8 value = saveData[index]; + trace("Flash read byte in state read: index {:08X} = {:02X}", index, value); + return value; + } - panic("Accessing flash when not mapped!"); + panic("Accessing flash when not mapped!"); - case FlashState::Status: - { - const u32 offset = (7 - (index % 8)) * 8; - const u8 value = (status >> offset) & 0xFF; - trace("Flash read byte in state status: index {:08X} = {:02X}", index, value); - return value; + case FlashState::Status: + { + const u32 offset = (7 - (index % 8)) * 8; + const u8 value = (status >> offset) & 0xFF; + trace("Flash read byte in state status: index {:08X} = {:02X}", index, value); + return value; + } + default: + panic("Flash read byte while in unknown state"); + return 0; } - default: - panic("Flash read byte while in unknown state"); - return 0; - } } template <> u32 Flash::Read(u32) const { - return status >> 32; + return status >> 32; } } // namespace n64 diff --git a/src/backend/core/mmio/AI.cpp b/src/backend/core/mmio/AI.cpp index 1abdd8b..9129c83 100644 --- a/src/backend/core/mmio/AI.cpp +++ b/src/backend/core/mmio/AI.cpp @@ -1,119 +1,122 @@ #include #include +#include namespace n64 { AI::AI() { Reset(); } void AI::Reset() { - dmaEnable = false; - dacRate = 0; - bitrate = 0; - dmaCount = 0; - dmaAddrCarry = false; - cycles = 0; - dmaLen = {}; - dmaAddr = {}; - dac = {44100, N64_CPU_FREQ / dac.freq, 16}; - device.Reset(); + dmaEnable = false; + dacRate = 0; + bitrate = 0; + dmaCount = 0; + dmaAddrCarry = false; + cycles = 0; + dmaLen = {}; + dmaAddr = {}; + dac = {44100, N64_CPU_FREQ / dac.freq, 16}; + device.Reset(); } // https://github.com/ares-emulator/ares/blob/master/ares/n64/ai/io.cpp // https://github.com/ares-emulator/ares/blob/master/LICENSE auto AI::Read(const u32 addr) const -> u32 { - if (addr == 0x0450000C) { - u32 val = 0; - val |= (dmaCount > 1); - val |= 1 << 20; - val |= 1 << 24; - val |= (dmaEnable << 25); - val |= (dmaCount > 0) << 30; - val |= (dmaCount > 1) << 31; - return val; - } + if (addr == 0x0450000C) { + u32 val = 0; + val |= (dmaCount > 1); + val |= 1 << 20; + val |= 1 << 24; + val |= (dmaEnable << 25); + val |= (dmaCount > 0) << 30; + val |= (dmaCount > 1) << 31; + return val; + } - return dmaLen[0]; + return dmaLen[0]; } void AI::Write(const u32 addr, const u32 val) { - n64::Mem& mem = n64::Core::GetMem(); - switch (addr) { - case 0x04500000: - if (dmaCount < 2) { - dmaAddr[dmaCount] = val & 0xFFFFFF & ~7; + n64::Mem &mem = n64::Core::GetMem(); + switch (addr) { + case 0x04500000: + if (dmaCount < 2) { + dmaAddr[dmaCount] = val & 0xFFFFFF & ~7; + } + break; + case 0x04500004: + { + const u32 len = (val & 0x3FFFF) & ~7; + if (dmaCount < 2) { + if (dmaCount == 0) + mem.mmio.mi.InterruptRaise(MI::Interrupt::AI); + dmaLen[dmaCount] = len; + dmaCount++; + } + } + break; + case 0x04500008: + dmaEnable = val & 1; + break; + case 0x0450000C: + mem.mmio.mi.InterruptLower(MI::Interrupt::AI); + break; + case 0x04500010: + { + const u32 oldDacFreq = dac.freq; + dacRate = val & 0x3FFF; + dac.freq = std::max(1.f, (float)GetVideoFrequency(mem.IsROMPAL()) / (dacRate + 1)) * 1.037; + dac.period = GetVideoFrequency(mem.IsROMPAL()) / dac.freq; + if (oldDacFreq != dac.freq) { + device.AdjustSampleRate(dac.freq); + } + } + break; + case 0x04500014: + bitrate = val & 0xF; + dac.precision = bitrate + 1; + break; + default: + panic("Unhandled AI write at addr {:08X} with val {:08X}", addr, val); } - break; - case 0x04500004: - { - const u32 len = (val & 0x3FFFF) & ~7; - if (dmaCount < 2) { - if (dmaCount == 0) - mem.mmio.mi.InterruptRaise(MI::Interrupt::AI); - dmaLen[dmaCount] = len; - dmaCount++; - } - } - break; - case 0x04500008: - dmaEnable = val & 1; - break; - case 0x0450000C: - mem.mmio.mi.InterruptLower(MI::Interrupt::AI); - break; - case 0x04500010: - { - const u32 oldDacFreq = dac.freq; - dacRate = val & 0x3FFF; - dac.freq = std::max(1.f, (float)GetVideoFrequency(mem.IsROMPAL()) / (dacRate + 1)) * 1.037; - dac.period = GetVideoFrequency(mem.IsROMPAL()) / dac.freq; - if (oldDacFreq != dac.freq) { - device.AdjustSampleRate(dac.freq); - } - } - break; - case 0x04500014: - bitrate = val & 0xF; - dac.precision = bitrate + 1; - break; - default: - panic("Unhandled AI write at addr {:08X} with val {:08X}", addr, val); - } } -void AI::Step(const u32 cpuCycles, const float volumeL, const float volumeR) { - n64::Mem& mem = n64::Core::GetMem(); - cycles += cpuCycles; - while (cycles > dac.period) { - if (dmaCount == 0) { - return; +void AI::Step(const u32 cpuCycles) { + n64::Mem &mem = n64::Core::GetMem(); + cycles += cpuCycles; + while (cycles > dac.period) { + if (dmaCount == 0) { + return; + } + + if (dmaLen[0] && dmaEnable) { + const u32 addrHi = (dmaAddr[0] >> 13) + dmaAddrCarry & 0x7FF; + dmaAddr[0] = addrHi << 13 | dmaAddr[0] & 0x1FFF; + const u32 data = mem.mmio.rdp.ReadRDRAM(dmaAddr[0]); + const s16 l = s16(data >> 16); + const s16 r = s16(data); + + auto volume = Options::GetVolume(); + if (volume > 0) { + device.PushSample((float)l / std::numeric_limits::max(), volume, + (float)r / std::numeric_limits::max(), volume); + } + + const u32 addrLo = dmaAddr[0] + 4 & 0x1FFF; + dmaAddr[0] = dmaAddr[0] & ~0x1FFF | addrLo; + dmaAddrCarry = addrLo == 0; + dmaLen[0] -= 4; + } + + if (!dmaLen[0]) { + if (--dmaCount > 0) { + mem.mmio.mi.InterruptRaise(MI::Interrupt::AI); + dmaAddr[0] = dmaAddr[1]; + dmaLen[0] = dmaLen[1]; + } + } + + cycles -= dac.period; } - - if (dmaLen[0] && dmaEnable) { - const u32 addrHi = (dmaAddr[0] >> 13) + dmaAddrCarry & 0x7FF; - dmaAddr[0] = addrHi << 13 | dmaAddr[0] & 0x1FFF; - const u32 data = mem.mmio.rdp.ReadRDRAM(dmaAddr[0]); - const s16 l = s16(data >> 16); - const s16 r = s16(data); - - if (volumeR > 0 && volumeL > 0) { - device.PushSample((float)l / std::numeric_limits::max(), volumeL, (float)r / std::numeric_limits::max(), volumeR); - } - - const u32 addrLo = dmaAddr[0] + 4 & 0x1FFF; - dmaAddr[0] = dmaAddr[0] & ~0x1FFF | addrLo; - dmaAddrCarry = addrLo == 0; - dmaLen[0] -= 4; - } - - if (!dmaLen[0]) { - if (--dmaCount > 0) { - mem.mmio.mi.InterruptRaise(MI::Interrupt::AI); - dmaAddr[0] = dmaAddr[1]; - dmaLen[0] = dmaLen[1]; - } - } - - cycles -= dac.period; - } } } // namespace n64 diff --git a/src/backend/core/mmio/AI.hpp b/src/backend/core/mmio/AI.hpp index 1585bba..86edb14 100644 --- a/src/backend/core/mmio/AI.hpp +++ b/src/backend/core/mmio/AI.hpp @@ -1,29 +1,30 @@ #pragma once #include #include +#include namespace n64 { struct AI { - AI(); - void Reset(); - auto Read(u32) const -> u32; - void Write(u32, u32); - void Step(u32, float, float); - bool dmaEnable{}; - bool dmaAddrCarry{}; - u8 bitrate{}; - u16 dacRate{}; - int dmaCount{}; - u32 cycles{}; - std::array dmaLen{}; - std::array dmaAddr{}; - - struct { - u32 freq{44100}; - u32 period{N64_CPU_FREQ / freq}; - u32 precision{16}; - } dac; - - AudioDevice device; + AI(); + void Reset(); + auto Read(u32) const -> u32; + void Write(u32, u32); + void Step(u32); + bool dmaEnable{}; + bool dmaAddrCarry{}; + u8 bitrate{}; + u16 dacRate{}; + int dmaCount{}; + u32 cycles{}; + std::array dmaLen{}; + std::array dmaAddr{}; + + struct { + u32 freq{44100}; + u32 period{N64_CPU_FREQ / freq}; + u32 precision{16}; + } dac; + + AudioDevice device; }; } // namespace n64 diff --git a/src/backend/core/mmio/PIF.cpp b/src/backend/core/mmio/PIF.cpp index 51c1511..ace3aa2 100644 --- a/src/backend/core/mmio/PIF.cpp +++ b/src/backend/core/mmio/PIF.cpp @@ -36,7 +36,7 @@ void PIF::Reset() { void PIF::MaybeLoadMempak() { if (!mempakOpen) { fs::path mempakPath_ = mempakPath; - fs::path savePath = Options::GetInstance().GetValue("general", "savePath"); + fs::path savePath = Options::GetSavesPath(); if (!savePath.empty()) { if (!fs::exists(savePath)) fs::create_directory(savePath); @@ -92,7 +92,7 @@ FORCE_INLINE size_t GetSaveSize(SaveType saveType) { void PIF::LoadEeprom(const SaveType saveType, const std::string &path) { if (saveType == SAVE_EEPROM_16k || saveType == SAVE_EEPROM_4k) { fs::path eepromPath_ = path; - std::string savePath = Options::GetInstance().GetValue("general", "savePath"); + std::string savePath = Options::GetSavesPath(); if (!savePath.empty()) { eepromPath_ = savePath / eepromPath_.filename(); } diff --git a/src/frontend/EmuThread.cpp b/src/frontend/EmuThread.cpp index 0a83ce7..1108bbe 100644 --- a/src/frontend/EmuThread.cpp +++ b/src/frontend/EmuThread.cpp @@ -1,53 +1,25 @@ -#include -#include +#include #include +#include -EmuThread::EmuThread(double &fps, SettingsWindow &settings) noexcept : settings(settings), fps(fps) {} +EmuThread::EmuThread() noexcept {} void EmuThread::run() const noexcept { - n64::Core& core = n64::Core::GetInstance(); - if(!core.romLoaded) return; - - auto lastSample = std::chrono::high_resolution_clock::now(); - auto avgFps = 16.667; - auto sampledFps = 0; - static bool oneSecondPassed = false; + n64::Core &core = n64::Core::GetInstance(); + if (!core.romLoaded) + return; - fps = 1000.0 / avgFps; - - const auto startFrameTime = std::chrono::high_resolution_clock::now(); - if (!core.pause) { - core.Run(settings.getVolumeL(), settings.getVolumeR()); - } - - const auto endFrameTime = std::chrono::high_resolution_clock::now(); - using namespace std::chrono_literals; - const auto frameTimeMs = std::chrono::duration(endFrameTime - startFrameTime) / 1ms; - avgFps += frameTimeMs; - - sampledFps++; - - if (const auto elapsedSinceLastSample = std::chrono::duration(endFrameTime - lastSample) / 1s; - elapsedSinceLastSample >= 1.0) { - if (!oneSecondPassed) { - oneSecondPassed = true; - return; + if (!core.pause) { + core.Run(); } - avgFps /= sampledFps; - fps = 1000.0 / avgFps; - } } -void EmuThread::TogglePause() const noexcept { - n64::Core::GetInstance().TogglePause(); -} +void EmuThread::TogglePause() const noexcept { n64::Core::GetInstance().TogglePause(); } -void EmuThread::Reset() const noexcept { - n64::Core::GetInstance().Reset(); -} +void EmuThread::Reset() const noexcept { n64::Core::GetInstance().Reset(); } void EmuThread::Stop() const noexcept { - n64::Core& core = n64::Core::GetInstance(); - core.Stop(); - core.rom = {}; + n64::Core &core = n64::Core::GetInstance(); + core.Stop(); + core.rom = {}; } diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index f176482..c2b824f 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -2,360 +2,53 @@ #include #include #include +#include #include -KaizenGui::KaizenGui() noexcept : vulkanWidget(windowHandle()) { +KaizenGui::KaizenGui() noexcept { SDL_InitSubSystem(SDL_INIT_GAMEPAD); + + hide(); + vulkanWidget = new RenderWidget(); + + setWindowTitle("Kaizen " KAIZEN_VERSION_STR); + setMinimumSize(640, 480); + setCentralWidget(vulkanWidget); + auto fileMenu = menuBar()->addMenu("File"); - auto open = fileMenu->addMenu("Open"); - auto exit = fileMenu->addMenu("Exit"); + auto open = fileMenu->addAction("Open"); + auto exit = fileMenu->addAction("Exit"); auto emulationMenu = menuBar()->addMenu("Emulation"); - auto settingsMenu = emulationMenu->addMenu("Settings"); - connect(settingsMenu, &QMenu::triggered, this, [&] { settingsWindow.show(); }); + auto settingsMenu = emulationMenu->addAction("Settings"); + settingsWindow = new SettingsWindow(); + connect(settingsMenu, &QAction::triggered, settingsWindow, &SettingsWindow::show); emulationMenu->addSeparator(); - auto pause = emulationMenu->addMenu("Pause"); - auto reset = emulationMenu->addMenu("Reset"); - auto stop = emulationMenu->addMenu("Stop"); + auto pause = emulationMenu->addAction("Pause"); + auto reset = emulationMenu->addAction("Reset"); + auto stop = emulationMenu->addAction("Stop"); auto helpMenu = menuBar()->addMenu("Help"); + auto about = helpMenu->addAction("About"); + connect(about, &QAction::triggered, this, [&] { + auto text = std::format("

Kaizen is a Nintendo 64 emulator that strives
" + "to offer a friendly user experience and compatibility.
" + "Kaizen is licensed under the BSD 3-clause license.
" + "Nintendo 64 is a registered trademark of Nintendo Co., Ltd.


" + "Kaizen {}{}", + KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); + + QMessageBox::about(this, "About", text.c_str()); + }); + show(); } KaizenGui::~KaizenGui() { SDL_Quit(); } -void KaizenGui::QueryDevices(const SDL_Event &event) { - switch (event.type) { - case SDL_EVENT_GAMEPAD_ADDED: - if (!gamepad) { - const auto index = event.gdevice.which; - - gamepad = SDL_OpenGamepad(index); - info("Found controller!"); - info("Name: {}", SDL_GetGamepadName(gamepad)); - info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); - } - break; - case SDL_EVENT_GAMEPAD_REMOVED: - if (gamepad) - SDL_CloseGamepad(gamepad); - break; - default: - break; - } -} - -void KaizenGui::HandleInput(const SDL_Event &event) { - const n64::Core &core = n64::Core::GetInstance(); - n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; - switch (event.type) { - case SDL_EVENT_GAMEPAD_AXIS_MOTION: - if (!gamepad) - break; - { - pif.UpdateButton(0, n64::Controller::Key::Z, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); - pif.UpdateButton(0, n64::Controller::Key::CUp, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CDown, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); - pif.UpdateButton(0, n64::Controller::Key::CLeft, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CRight, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); - - float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); - if (xclamped < 0) { - xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); - } else { - xclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - xclamped *= 86; - - float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); - if (yclamped < 0) { - yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); - } else { - yclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - yclamped *= 86; - - pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); - pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast(xclamped)); - } - break; - case SDL_EVENT_GAMEPAD_BUTTON_DOWN: - case SDL_EVENT_GAMEPAD_BUTTON_UP: - if (!gamepad) - break; - - pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); - pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); - pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); - pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); - pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); - pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); - pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); - pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); - pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); - break; - case SDL_EVENT_KEY_DOWN: - case SDL_EVENT_KEY_UP: - { - const auto keys = SDL_GetKeyboardState(nullptr); - if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { - fileDialogOpen = true; - } - - fastForward = keys[SDL_SCANCODE_SPACE]; - if (!unlockFramerate) - core.parallel.SetFramerateUnlocked(fastForward); - - if (core.romLoaded) { - if (keys[SDL_SCANCODE_P]) { - emuThread.TogglePause(); - } - - if (keys[SDL_SCANCODE_R]) { - emuThread.Reset(); - } - - if (keys[SDL_SCANCODE_Q]) { - emuThread.Stop(); - } - } - - if (gamepad) - break; - - pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); - pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); - pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); - pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); - pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); - pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); - pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); - pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); - pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); - pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); - pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); - pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); - pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); - pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); - - float x = 0, y = 0; - - if (keys[SDL_SCANCODE_UP]) - y = 86; - if (keys[SDL_SCANCODE_DOWN]) - y = -86; - if (keys[SDL_SCANCODE_LEFT]) - x = -86; - if (keys[SDL_SCANCODE_RIGHT]) - x = 86; - - pif.UpdateAxis(0, n64::Controller::Axis::X, x); - pif.UpdateAxis(0, n64::Controller::Axis::Y, y); - } - break; - default: - break; - } -} - -void KaizenGui::RenderUI() { - n64::Core &core = n64::Core::GetInstance(); - gui::StartFrame(); - - if (ImGui::BeginMainMenuBar()) { - if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Open", "Ctrl-O")) { - fileDialogOpen = true; - } - if (ImGui::MenuItem("Exit")) { - quit = true; - emuThread.Stop(); - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Emulation")) { - ImGui::BeginDisabled(!core.romLoaded); - - if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { - emuThread.TogglePause(); - } - - if (ImGui::MenuItem("Reset", "R")) { - emuThread.Reset(); - } - - if (ImGui::MenuItem("Stop", "Q")) { - emuThread.Stop(); - core.romLoaded = false; - } - - if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { - core.parallel.SetFramerateUnlocked(unlockFramerate); - } - - if (ImGui::MenuItem("Open Debugger")) { - debugger.Open(); - } - - ImGui::EndDisabled(); - - if (ImGui::MenuItem("Options")) { - settingsWindow.isOpen = true; - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Help")) { - if (ImGui::MenuItem("About")) { - aboutOpen = true; - } - - ImGui::EndMenu(); - } - ImGui::EndMainMenuBar(); - } - - if (settingsWindow.isOpen) { - ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); - } - - if (aboutOpen) { - ImGui::OpenPopup("About Kaizen"); - } - - settingsWindow.render(); - debugger.render(); - - const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); - ImGui::Text("to offer a friendly user experience and compatibility."); - ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); - ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); - ImGui::Separator(); - ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); - ImGui::Separator(); - if (ImGui::Button("OK")) { - aboutOpen = false; - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } - - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - if (ImGui::BeginMainStatusBar()) { - ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); - ImGui::EndMainStatusBar(); - } - - if (shouldDisplaySpinner) { - ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, - ImVec2(0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - - ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); - - ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); - ImGui::SameLine(); - - ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); - ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); - ImGui::PopFont(); - - ImGui::End(); - - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - } - - ImGui::Render(); - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - } - - if (fileDialogOpen) { - fileDialogOpen = false; - constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, - {"Nintendo 64 executable", "n64;z64;v64"}, - {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; - SDL_ShowOpenFileDialog( - [](void *userdata, const char *const *filelist, int) { - auto kaizen = static_cast(userdata); - - if (!filelist) { - panic("An error occured: {}", SDL_GetError()); - } - - if (!*filelist) { - warn("The user did not select any file."); - warn("Most likely, the dialog was canceled."); - return; - } - - kaizen->fileToLoad = *filelist; - kaizen->shouldDisplaySpinner = true; - std::thread fileWorker(&KaizenGui::FileWorker, kaizen); - fileWorker.detach(); - }, - this, window.getHandle(), filters, 3, nullptr, false); - } - - if (minimized) - return; - - if (core.romLoaded) { - core.parallel.UpdateScreen(); - return; - } - - core.parallel.UpdateScreen(); -} - void KaizenGui::LoadROM(const std::string &path) noexcept { n64::Core &core = n64::Core::GetInstance(); core.LoadROM(path); const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; - SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); -} - -void KaizenGui::run() { - while (!quit) { - SDL_Event e; - while (SDL_PollEvent(&e)) { - ImGui_ImplSDL3_ProcessEvent(&e); - switch (e.type) { - case SDL_EVENT_QUIT: - quit = true; - emuThread.Stop(); - break; - case SDL_EVENT_WINDOW_MINIMIZED: - minimized = true; - break; - case SDL_EVENT_WINDOW_RESTORED: - minimized = false; - break; - default: - break; - } - QueryDevices(e); - HandleInput(e); - } - - SDL_GetWindowSize(window.getHandle(), &width, &height); - - emuThread.run(); - RenderUI(); - } + setWindowTitle(("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); } void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); } diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index 63d8d2d..4666b9d 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -1,26 +1,24 @@ #pragma once -#include #include #include #include +#include class KaizenGui final : QMainWindow { public: explicit KaizenGui() noexcept; ~KaizenGui(); - double fpsCounter = -1.0; bool fastForward = false; bool unlockFramerate = false; bool minimized = false; - SettingsWindow settingsWindow; - RenderWidget vulkanWidget; + SettingsWindow *settingsWindow; + RenderWidget *vulkanWidget; EmuThread emuThread; SDL_Gamepad *gamepad = nullptr; - void run(); static void LoadTAS(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept; @@ -31,9 +29,6 @@ class KaizenGui final : QMainWindow { bool quit = false; bool shouldDisplaySpinner = false; std::string fileToLoad = ""; - void RenderUI(); - void HandleInput(const SDL_Event &event); - void QueryDevices(const SDL_Event &event); void FileWorker() { if (fileToLoad.empty()) diff --git a/src/frontend/RenderWidget.cpp b/src/frontend/RenderWidget.cpp index f9f4797..e6d67eb 100644 --- a/src/frontend/RenderWidget.cpp +++ b/src/frontend/RenderWidget.cpp @@ -1,10 +1,49 @@ -#include #include #include +#include +#include -RenderWidget::RenderWidget(QWindow *window) { - wsiPlatform = std::make_shared(window); - windowInfo = std::make_shared(window); +enum class CompositorCategory { Windows, MacOS, XCB, Wayland }; + +static CompositorCategory GetOSCompositorCategory() { + const QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("windows")) + return CompositorCategory::Windows; + if (platform_name == QStringLiteral("xcb")) + return CompositorCategory::XCB; + if (platform_name == QStringLiteral("wayland") || platform_name == QStringLiteral("wayland-egl")) + return CompositorCategory::Wayland; + if (platform_name == QStringLiteral("cocoa") || platform_name == QStringLiteral("ios")) + return CompositorCategory::MacOS; + + warn("Unknown Qt platform!"); + return CompositorCategory::Windows; +} + +RenderWidget::RenderWidget() { + setAttribute(Qt::WA_NativeWindow); + setAttribute(Qt::WA_PaintOnScreen); + if (GetOSCompositorCategory() == CompositorCategory::Wayland) { + setAttribute(Qt::WA_DontCreateNativeAncestors); + } + + if (GetOSCompositorCategory() == CompositorCategory::MacOS) { + windowHandle()->setSurfaceType(QWindow::MetalSurface); + } else { + windowHandle()->setSurfaceType(QWindow::VulkanSurface); + } + + if (!Vulkan::Context::init_loader(nullptr)) { + panic("Could not initialize Vulkan ICD"); + } + + qtVkInstanceFactory = std::make_unique(); + + windowHandle()->setVulkanInstance(&qtVkInstanceFactory->handle); + windowHandle()->create(); + + wsiPlatform = std::make_shared(windowHandle()); + windowInfo = std::make_shared(windowHandle()); n64::Core &core = n64::Core::GetInstance(); - core.parallel.Init(wsiPlatform, windowInfo, core.GetMem().GetRDRAMPtr()); + core.parallel.Init(wsiPlatform, windowInfo, qtVkInstanceFactory.get(), core.GetMem().GetRDRAMPtr()); } diff --git a/src/frontend/RenderWidget.hpp b/src/frontend/RenderWidget.hpp index c0387d4..6edcfce 100644 --- a/src/frontend/RenderWidget.hpp +++ b/src/frontend/RenderWidget.hpp @@ -1,4 +1,5 @@ #pragma once +#undef signals #include #include #include @@ -8,12 +9,47 @@ struct InputSettings; namespace n64 { struct Core; } +class QtPrdpWindowInfo; +class QtWSIPlatform; + +struct QtInstanceFactory : Vulkan::InstanceFactory { + VkInstance create_instance(const VkInstanceCreateInfo *info) override { + handle.setApiVersion({1, 3, 0}); + QByteArrayList exts; + for (int i = 0; i < info->enabledExtensionCount; i++) { + exts.push_back(QByteArray::fromStdString(info->ppEnabledExtensionNames[i])); + } + QByteArrayList layers; + for (int i = 0; i < info->enabledLayerCount; i++) { + layers.push_back(QByteArray::fromStdString(info->ppEnabledLayerNames[i])); + } + handle.setExtensions(exts); + handle.setLayers(layers); + handle.create(); + + return handle.vkInstance(); + } + + QVulkanInstance handle; +}; + +class RenderWidget final : public QWidget { + QVulkanInstance inst; + + public: + explicit RenderWidget(); + + std::unique_ptr qtVkInstanceFactory; + std::shared_ptr windowInfo; + std::shared_ptr wsiPlatform; +}; + class QtPrdpWindowInfo final : public ParallelRDP::WindowInfo { public: explicit QtPrdpWindowInfo(QWindow *window) : window(window) {} CoordinatePair get_window_size() override { - return CoordinatePair{static_cast(window->size().width()), static_cast(window->size().height())}; + return CoordinatePair{static_cast(window->width()), static_cast(window->height())}; } private: @@ -27,12 +63,11 @@ class QtWSIPlatform final : public Vulkan::WSIPlatform { std::vector get_instance_extensions() override { auto vec = std::vector(); - u32 extCount; auto extensions = window->vulkanInstance()->extensions(); vec.resize(extensions.size()); - for (u32 i = 0; i < extCount; i++) { + for (u32 i = 0; i < extensions.size(); i++) { vec[i] = extensions[i]; } @@ -40,7 +75,7 @@ class QtWSIPlatform final : public Vulkan::WSIPlatform { } VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice pDevice) override { - return (surface = QVulkanInstance::surfaceForWindow(window)); + return QVulkanInstance::surfaceForWindow(window); } void destroy_surface(VkInstance instance, VkSurfaceKHR surface) override {} @@ -61,16 +96,7 @@ class QtWSIPlatform final : public Vulkan::WSIPlatform { VkApplicationInfo appInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_3}; QWindow *window{}; - VkSurfaceKHR surface; private: bool gamepadConnected = false; }; - -class RenderWidget final : QVulkanWindow { - public: - explicit RenderWidget(QWindow *); - - std::shared_ptr windowInfo; - std::shared_ptr wsiPlatform; -}; diff --git a/src/frontend/Settings/GeneralSettings.cpp b/src/frontend/Settings/GeneralSettings.cpp index 8161a48..0e6984f 100644 --- a/src/frontend/Settings/GeneralSettings.cpp +++ b/src/frontend/Settings/GeneralSettings.cpp @@ -1,9 +1,9 @@ #include #include -#include #include #include #include +#include GeneralSettings::GeneralSettings() { QSettings settings; @@ -18,10 +18,10 @@ GeneralSettings::GeneralSettings() { QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); selectedFolderLabel->setText(dir); Options::SetSavesPath(dir.toStdString()); - settings.setValue("general/saves_path", dir); + settings.setValue("general/savesPath", dir); }); - h = new QHBoxLayout(this); + h = new QHBoxLayout(); h->addWidget(description); h->addWidget(selectedFolderLabel); diff --git a/src/frontend/SettingsWindow.cpp b/src/frontend/SettingsWindow.cpp index db0111a..e37bcf4 100644 --- a/src/frontend/SettingsWindow.cpp +++ b/src/frontend/SettingsWindow.cpp @@ -4,11 +4,14 @@ SettingsWindow::SettingsWindow() { hide(); - QSettings settings; + + general = new GeneralSettings(); + cpu = new CPUSettings(); + audio = new AudioSettings(); categories = new QTabWidget(this); - categories->addTab(&general, "General"); - categories->addTab(&cpu, "MIPS VR4300i"); - categories->addTab(&audio, "Audio"); + categories->addTab(general, "General"); + categories->addTab(cpu, "MIPS VR4300i"); + categories->addTab(audio, "Audio"); v = new QVBoxLayout(this); v->addWidget(categories); diff --git a/src/frontend/SettingsWindow.hpp b/src/frontend/SettingsWindow.hpp index 31853a6..e98e698 100644 --- a/src/frontend/SettingsWindow.hpp +++ b/src/frontend/SettingsWindow.hpp @@ -7,14 +7,13 @@ #include #include -class SettingsWindow final : QWidget { +class SettingsWindow final : public QWidget { QTabWidget *categories; - GeneralSettings general; - AudioSettings audio; - CPUSettings cpu; + GeneralSettings *general; + AudioSettings *audio; + CPUSettings *cpu; QVBoxLayout *v; public: SettingsWindow(); - void show() { QWidget::show(); } }; diff --git a/src/frontend/main.cpp b/src/frontend/main.cpp index 3f58743..65ff168 100644 --- a/src/frontend/main.cpp +++ b/src/frontend/main.cpp @@ -1,23 +1,14 @@ -#include #include #include +#include +#include -int main(const int argc, char **argv) { +int main(int argc, char **argv) { QCoreApplication::setOrganizationName("kaizen"); QCoreApplication::setOrganizationDomain("irco.sh"); QCoreApplication::setApplicationName("Kaizen"); + QApplication app(argc, argv); KaizenGui kaizenGui; - cflags::cflags flags; - flags.add_string_callback( - '\0', "rom", [&kaizenGui](const std::string &v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line"); - flags.add_string_callback( - '\0', "movie", [](const std::string &v) { KaizenGui::LoadTAS(v); }, "Mupen Movie to replay"); - - if (!flags.parse(argc, argv)) - return -1; - - kaizenGui.run(); - - return 0; + return app.exec(); }