From 92897fe9ce959d11b27132e6a262835f19594f19 Mon Sep 17 00:00:00 2001 From: CocoSimone Date: Mon, 27 Jun 2022 00:17:04 +0200 Subject: [PATCH] Migration of shibumi --- .gitmodules | 6 + external/CMakeLists.txt | 60 ++++- external/parallel-rdp-standalone | 1 + src/core/BaseCore.cpp | 1 + src/core/BaseCore.hpp | 7 +- src/core/CMakeLists.txt | 3 +- src/core/gb/Core.cpp | 7 + src/core/gb/Core.hpp | 1 + src/core/n64/CMakeLists.txt | 10 +- src/core/n64/Core.cpp | 11 +- src/core/n64/Core.hpp | 8 +- src/core/n64/Cpu.cpp | 1 - src/core/n64/core/CMakeLists.txt | 13 ++ src/core/n64/core/Cpu.cpp | 1 + src/core/n64/core/Cpu.hpp | 8 + src/core/n64/{ => core}/Mem.cpp | 8 +- src/core/n64/{ => core}/Mem.hpp | 9 +- src/core/n64/core/RDP.cpp | 1 + src/core/n64/{Cpu.hpp => core/RDP.hpp} | 7 +- src/core/n64/core/cpu/CMakeLists.txt | 11 + src/core/n64/core/cpu/Registers.cpp | 1 + src/core/n64/core/cpu/Registers.hpp | 9 + src/core/n64/core/cpu/registers/Cop0.cpp | 17 ++ src/core/n64/core/cpu/registers/Cop0.hpp | 168 ++++++++++++++ src/core/util.hpp | 14 +- src/frontend/qt/CMakeLists.txt | 2 +- src/frontend/qt/Frontend.cpp | 111 ++++++++- src/frontend/qt/Frontend.hpp | 63 ++++- src/frontend/qt/main.cpp | 10 +- src/frontend/sdl/CMakeLists.txt | 8 +- src/frontend/sdl/Frontend.cpp | 22 +- src/frontend/sdl/Frontend.hpp | 5 +- src/frontend/sdl/ParallelRDPWrapper.cpp | 282 +++++++++++++++++++++++ src/frontend/sdl/ParallelRDPWrapper.hpp | 19 ++ 34 files changed, 853 insertions(+), 52 deletions(-) create mode 160000 external/parallel-rdp-standalone delete mode 100644 src/core/n64/Cpu.cpp create mode 100644 src/core/n64/core/CMakeLists.txt create mode 100644 src/core/n64/core/Cpu.cpp create mode 100644 src/core/n64/core/Cpu.hpp rename src/core/n64/{ => core}/Mem.cpp (78%) rename src/core/n64/{ => core}/Mem.hpp (52%) create mode 100644 src/core/n64/core/RDP.cpp rename src/core/n64/{Cpu.hpp => core/RDP.hpp} (63%) create mode 100644 src/core/n64/core/cpu/CMakeLists.txt create mode 100644 src/core/n64/core/cpu/Registers.cpp create mode 100644 src/core/n64/core/cpu/Registers.hpp create mode 100644 src/core/n64/core/cpu/registers/Cop0.cpp create mode 100644 src/core/n64/core/cpu/registers/Cop0.hpp create mode 100644 src/frontend/sdl/ParallelRDPWrapper.cpp create mode 100644 src/frontend/sdl/ParallelRDPWrapper.hpp diff --git a/.gitmodules b/.gitmodules index e69de29b..3802097b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "external/volk"] + path = external/volk + url = https://github.com/zeux/volk +[submodule "external/parallel-rdp-standalone"] + path = external/parallel-rdp-standalone + url = https://github.com/Themaister/parallel-rdp-standalone diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index a4d17a75..513d6555 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,3 +1,61 @@ cmake_minimum_required(VERSION 3.20) +project(parallel-rdp) -include_directories(.) \ No newline at end of file +file(GLOB_RECURSE parallel-rdp-cpp parallel-rdp-standalone/parallel-rdp/*.cpp) + +add_library(parallel-rdp + ${parallel-rdp-cpp} + parallel-rdp-standalone/vulkan/buffer.cpp + parallel-rdp-standalone/vulkan/buffer_pool.cpp + parallel-rdp-standalone/vulkan/command_buffer.cpp + parallel-rdp-standalone/vulkan/command_pool.cpp + parallel-rdp-standalone/vulkan/context.cpp + parallel-rdp-standalone/vulkan/cookie.cpp + parallel-rdp-standalone/vulkan/descriptor_set.cpp + parallel-rdp-standalone/vulkan/device.cpp + parallel-rdp-standalone/vulkan/event_manager.cpp + parallel-rdp-standalone/vulkan/fence.cpp + parallel-rdp-standalone/vulkan/fence_manager.cpp + parallel-rdp-standalone/vulkan/image.cpp + parallel-rdp-standalone/vulkan/memory_allocator.cpp + parallel-rdp-standalone/vulkan/pipeline_event.cpp + parallel-rdp-standalone/vulkan/query_pool.cpp + parallel-rdp-standalone/vulkan/render_pass.cpp + parallel-rdp-standalone/vulkan/sampler.cpp + parallel-rdp-standalone/vulkan/semaphore.cpp + parallel-rdp-standalone/vulkan/semaphore_manager.cpp + parallel-rdp-standalone/vulkan/shader.cpp + parallel-rdp-standalone/vulkan/texture_format.cpp + parallel-rdp-standalone/vulkan/wsi.cpp + parallel-rdp-standalone/vulkan/wsi.hpp + parallel-rdp-standalone/vulkan/wsi_timing.cpp + parallel-rdp-standalone/vulkan/wsi_timing.hpp + parallel-rdp-standalone/util/aligned_alloc.cpp + parallel-rdp-standalone/util/timer.cpp + parallel-rdp-standalone/util/timeline_trace_file.cpp + parallel-rdp-standalone/util/timeline_trace_file.hpp + parallel-rdp-standalone/util/thread_name.cpp + parallel-rdp-standalone/util/thread_name.hpp + parallel-rdp-standalone/util/logging.cpp + parallel-rdp-standalone/util/logging.hpp + parallel-rdp-standalone/util/thread_id.cpp + parallel-rdp-standalone/util/thread_id.hpp + # C + parallel-rdp-standalone/volk/volk.c +) + +target_compile_definitions(parallel-rdp PUBLIC GRANITE_VULKAN_MT) + +target_include_directories(parallel-rdp PUBLIC + parallel-rdp-standalone/parallel-rdp + parallel-rdp-standalone/volk + parallel-rdp-standalone/spirv-cross + parallel-rdp-standalone/vulkan + parallel-rdp-standalone/vulkan-headers/include + parallel-rdp-standalone/util) + +if(WIN32) + target_compile_definitions(parallel-rdp PUBLIC VK_USE_PLATFORM_WIN32_KHR) +else() + target_link_libraries(parallel-rdp dl) +endif() \ No newline at end of file diff --git a/external/parallel-rdp-standalone b/external/parallel-rdp-standalone new file mode 160000 index 00000000..31e97d2f --- /dev/null +++ b/external/parallel-rdp-standalone @@ -0,0 +1 @@ +Subproject commit 31e97d2f215e2200956846246c23c7ba51767833 diff --git a/src/core/BaseCore.cpp b/src/core/BaseCore.cpp index 9bf637e0..dc3de2e6 100644 --- a/src/core/BaseCore.cpp +++ b/src/core/BaseCore.cpp @@ -2,4 +2,5 @@ namespace natsukashii::core { void BaseCore::Run() {} +void BaseCore::PollInputs(u32 windowID) {} } \ No newline at end of file diff --git a/src/core/BaseCore.hpp b/src/core/BaseCore.hpp index 1062c3ec..376d603b 100644 --- a/src/core/BaseCore.hpp +++ b/src/core/BaseCore.hpp @@ -1,8 +1,13 @@ #pragma once +#include namespace natsukashii::core { struct BaseCore { - virtual ~BaseCore() {} + virtual ~BaseCore() = default; virtual void Run(); + virtual void PollInputs(u32); + [[nodiscard]] bool& ShouldQuit() { return quit; } +private: + bool quit = false; }; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3988d0ad..45d52eee 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -12,6 +12,5 @@ add_library(cores Scheduler.hpp common.hpp util.hpp) -target_include_directories(cores PRIVATE .) -target_include_directories(cores PUBLIC ../../external) +target_include_directories(cores PUBLIC . ../../external) target_link_libraries(cores PUBLIC gb n64) diff --git a/src/core/gb/Core.cpp b/src/core/gb/Core.cpp index 5f3f66b3..5c994326 100644 --- a/src/core/gb/Core.cpp +++ b/src/core/gb/Core.cpp @@ -1,4 +1,5 @@ #include +#include namespace natsukashii::gb::core { Core::Core(const std::string& rom) { @@ -10,4 +11,10 @@ void Core::Run() { cpu.Step(mem); } } + +void Core::PollInputs(u32 windowID) { + SDL_Event event; + SDL_PollEvent(&event); + ShouldQuit() = event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == windowID; +} } diff --git a/src/core/gb/Core.hpp b/src/core/gb/Core.hpp index 61395291..25d6e2d7 100644 --- a/src/core/gb/Core.hpp +++ b/src/core/gb/Core.hpp @@ -10,6 +10,7 @@ struct Core : BaseCore { ~Core() override = default; explicit Core(const std::string&); void Run() override; + void PollInputs(u32) override; private: Mem mem; Cpu cpu; diff --git a/src/core/n64/CMakeLists.txt b/src/core/n64/CMakeLists.txt index 5bc85ba9..ab9d4b24 100644 --- a/src/core/n64/CMakeLists.txt +++ b/src/core/n64/CMakeLists.txt @@ -18,16 +18,14 @@ FetchContent_Declare( FetchContent_MakeAvailable(mio toml11) find_package(fmt REQUIRED) +add_subdirectory(core) + add_library(n64 Core.hpp Core.cpp - Cpu.hpp - Cpu.cpp - Mem.cpp - Mem.hpp memory_regions.hpp ../BaseCore.cpp ../BaseCore.hpp) -target_include_directories(n64 PRIVATE . .. ../../../external) +target_include_directories(n64 PRIVATE . ..) target_include_directories(n64 PUBLIC ${mio_SOURCE_DIR}/include ${toml11_SOURCE_DIR}/include) -target_link_libraries(n64 PRIVATE mio::mio toml11::toml11 fmt::fmt) +target_link_libraries(n64 PRIVATE mio::mio toml11::toml11 fmt::fmt n64-core) diff --git a/src/core/n64/Core.cpp b/src/core/n64/Core.cpp index e649ea76..f843e155 100644 --- a/src/core/n64/Core.cpp +++ b/src/core/n64/Core.cpp @@ -1,9 +1,18 @@ #include +#include namespace natsukashii::n64::core { -Core::Core(const std::string& rom) {} +Core::Core(const std::string& rom) { + mem.LoadROM(rom); +} void Core::Run() { } + +void Core::PollInputs(u32 windowID) { + SDL_Event event; + SDL_PollEvent(&event); + ShouldQuit() = event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == windowID; +} } diff --git a/src/core/n64/Core.hpp b/src/core/n64/Core.hpp index 4a49bdb5..c15e58ff 100644 --- a/src/core/n64/Core.hpp +++ b/src/core/n64/Core.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include -#include +#include "n64/core/Cpu.hpp" +#include "n64/core/Mem.hpp" #include namespace natsukashii::n64::core { @@ -10,5 +10,9 @@ struct Core : BaseCore { ~Core() override = default; explicit Core(const std::string&); void Run() override; + void PollInputs(u32) override; +private: + Mem mem; + Cpu cpu; }; } diff --git a/src/core/n64/Cpu.cpp b/src/core/n64/Cpu.cpp deleted file mode 100644 index 44ff5d7a..00000000 --- a/src/core/n64/Cpu.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/src/core/n64/core/CMakeLists.txt b/src/core/n64/core/CMakeLists.txt new file mode 100644 index 00000000..db57f8bd --- /dev/null +++ b/src/core/n64/core/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) +project(n64-core) + +add_subdirectory(cpu) + +add_library(n64-core + Cpu.hpp + Cpu.cpp + Mem.cpp + Mem.hpp RDP.cpp RDP.hpp) + +target_include_directories(n64-core PRIVATE . .. ../../) +target_link_libraries(n64-core PUBLIC n64-cpu) diff --git a/src/core/n64/core/Cpu.cpp b/src/core/n64/core/Cpu.cpp new file mode 100644 index 00000000..5a56991b --- /dev/null +++ b/src/core/n64/core/Cpu.cpp @@ -0,0 +1 @@ +#include "Cpu.hpp" diff --git a/src/core/n64/core/Cpu.hpp b/src/core/n64/core/Cpu.hpp new file mode 100644 index 00000000..2f3a32be --- /dev/null +++ b/src/core/n64/core/Cpu.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "n64/core/cpu/Registers.hpp" + +namespace natsukashii::n64::core { +struct Cpu { + Registers regs; +}; +} diff --git a/src/core/n64/Mem.cpp b/src/core/n64/core/Mem.cpp similarity index 78% rename from src/core/n64/Mem.cpp rename to src/core/n64/core/Mem.cpp index bf1652ed..dbbb5021 100644 --- a/src/core/n64/Mem.cpp +++ b/src/core/n64/core/Mem.cpp @@ -18,13 +18,13 @@ void Mem::LoadROM(const std::string& filename) { file.seekg(std::ios::end); auto size = file.tellg(); + auto size_adjusted = util::NextPow2(size); + romMask = size_adjusted - 1; file.seekg(std::ios::beg); std::vector rom; - rom.reserve(size); - rom.insert(rom.begin(), - std::istream_iterator(file), - std::istream_iterator()); + rom.reserve(size_adjusted); + file.read(reinterpret_cast(rom.data()), size); file.close(); util::SwapN64Rom(size, rom.data()); diff --git a/src/core/n64/Mem.hpp b/src/core/n64/core/Mem.hpp similarity index 52% rename from src/core/n64/Mem.hpp rename to src/core/n64/core/Mem.hpp index 568e4c81..c499355f 100644 --- a/src/core/n64/Mem.hpp +++ b/src/core/n64/core/Mem.hpp @@ -1,16 +1,17 @@ #pragma once #include -#include +#include #include namespace natsukashii::n64::core { struct Mem { + ~Mem() = default; Mem(); void LoadROM(const std::string&); private: std::vector cart, rdram, sram; - u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{}, pif_ram[PIF_RAM_SIZE]{}; - u8 pif_bootrom[PIF_BOOTROM_SIZE]{}; - size_t rom_mask; + u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{}, pifRam[PIF_RAM_SIZE]{}; + u8 pifBootrom[PIF_BOOTROM_SIZE]{}; + size_t romMask; }; } diff --git a/src/core/n64/core/RDP.cpp b/src/core/n64/core/RDP.cpp new file mode 100644 index 00000000..bacad69e --- /dev/null +++ b/src/core/n64/core/RDP.cpp @@ -0,0 +1 @@ +#include diff --git a/src/core/n64/Cpu.hpp b/src/core/n64/core/RDP.hpp similarity index 63% rename from src/core/n64/Cpu.hpp rename to src/core/n64/core/RDP.hpp index 13eb8b61..de7fd408 100644 --- a/src/core/n64/Cpu.hpp +++ b/src/core/n64/core/RDP.hpp @@ -1,7 +1,8 @@ #pragma once - namespace natsukashii::n64::core { -struct Cpu { + +struct RDP { }; -} + +} // natsukashii diff --git a/src/core/n64/core/cpu/CMakeLists.txt b/src/core/n64/core/cpu/CMakeLists.txt new file mode 100644 index 00000000..97c2e9bd --- /dev/null +++ b/src/core/n64/core/cpu/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.20) +project(n64-cpu) + +add_library(n64-cpu + Registers.cpp + Registers.hpp + registers/Cop0.cpp + registers/Cop0.hpp) + +target_include_directories(n64-cpu PRIVATE . .. ../../ ../../../) +target_include_directories(n64-cpu PUBLIC registers ../../../../../external) diff --git a/src/core/n64/core/cpu/Registers.cpp b/src/core/n64/core/cpu/Registers.cpp new file mode 100644 index 00000000..2b45b8a1 --- /dev/null +++ b/src/core/n64/core/cpu/Registers.cpp @@ -0,0 +1 @@ +#include diff --git a/src/core/n64/core/cpu/Registers.hpp b/src/core/n64/core/cpu/Registers.hpp new file mode 100644 index 00000000..fbbd5231 --- /dev/null +++ b/src/core/n64/core/cpu/Registers.hpp @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace natsukashii::n64::core { +struct Registers { + s64 gpr[32]; + Cop0 cop0; +}; +} diff --git a/src/core/n64/core/cpu/registers/Cop0.cpp b/src/core/n64/core/cpu/registers/Cop0.cpp new file mode 100644 index 00000000..26d89d05 --- /dev/null +++ b/src/core/n64/core/cpu/registers/Cop0.cpp @@ -0,0 +1,17 @@ +#include + +namespace natsukashii::n64::core { +Cop0::Cop0() { + +} + +template +T Cop0::GetReg(u8 index) { + +} + +template +void Cop0::SetReg(u8 index, T val) { + +} +} \ No newline at end of file diff --git a/src/core/n64/core/cpu/registers/Cop0.hpp b/src/core/n64/core/cpu/registers/Cop0.hpp new file mode 100644 index 00000000..1bd0cf3b --- /dev/null +++ b/src/core/n64/core/cpu/registers/Cop0.hpp @@ -0,0 +1,168 @@ +#pragma once +#include + +namespace natsukashii::n64::core { +#define STATUS_MASK 0xFF77FFFF + +struct Cpu; +struct Mem; + +union Cop0Cause { + u32 raw; + struct { + unsigned: 8; + unsigned interruptPending: 8; + unsigned: 16; + } __attribute__((__packed__)); + struct { + unsigned: 2; + unsigned exceptionCode: 5; + unsigned: 1; + unsigned ip0: 1; + unsigned ip1: 1; + unsigned ip2: 1; + unsigned ip3: 1; + unsigned ip4: 1; + unsigned ip5: 1; + unsigned ip6: 1; + unsigned ip7: 1; + unsigned: 12; + unsigned copError: 2; + unsigned: 1; + unsigned branchDelay: 1; + } __attribute__((__packed__)); +}; + +union Cop0Status { + struct { + unsigned ie: 1; + unsigned exl: 1; + unsigned erl: 1; + unsigned ksu: 2; + unsigned ux: 1; + unsigned sx: 1; + unsigned kx: 1; + unsigned im: 8; + unsigned ds: 9; + unsigned re: 1; + unsigned fr: 1; + unsigned rp: 1; + unsigned cu0: 1; + unsigned cu1: 1; + unsigned cu2: 1; + unsigned cu3: 1; + } __attribute__((__packed__)); + struct { + unsigned: 16; + unsigned de: 1; + unsigned ce: 1; + unsigned ch: 1; + unsigned: 1; + unsigned sr: 1; + unsigned ts: 1; + unsigned bev: 1; + unsigned: 1; + unsigned its: 1; + unsigned: 7; + } __attribute__((__packed__)); + u32 raw; +} __attribute__((__packed__)); + +union EntryLo { + u32 raw; + struct { + unsigned g: 1; + unsigned v: 1; + unsigned d: 1; + unsigned c: 3; + unsigned pfn: 20; + unsigned: 6; + }; +}; + +union EntryHi { + u64 raw; + struct { + u64 asid: 8; + u64: 5; + u64 vpn2: 27; + u64 fill: 22; + u64 r: 2; + } __attribute__((__packed__)); +}; + +union PageMask { + u32 raw; + struct { + unsigned: 13; + unsigned mask: 12; + unsigned: 7; + }; +}; + +struct TLBEntry { + EntryLo entryLo0, entryLo1; + EntryHi entryHi; + PageMask pageMask; +}; + +enum TLBError : u8 { + NONE, + MISS, + INVALID, + MODIFICATION, + DISALLOWED_ADDRESS +}; + +enum TLBAccessType { + LOAD, STORE +}; + +union Context { + u64 raw; + struct { + u64: 4; + u64 badvpn2: 19; + u64 ptebase: 41; + }; +}; + +union XContext { + u64 raw; + struct { + u64: 4; + u64 badvpn2: 27; + u64 r: 2; + u64 ptebase: 31; + } __attribute__((__packed__)); +}; + +struct Cop0 { + Cop0(); + + template + T GetReg(u8 index); + + template + void SetReg(u8 index, T val); + + PageMask pageMask; + EntryHi entryHi; + EntryLo entryLo0, entryLo1; + u32 index, random; + Context context; + u32 wired, r7; + u64 badVaddr, count; + u32 compare; + Cop0Status status; + Cop0Cause cause; + u64 EPC; + u32 PRId, Config, LLAddr, WatchLo, WatchHi; + XContext xcontext; + u32 r21, r22, r23, r24, r25, ParityError, CacheError, TagLo, TagHi; + u64 ErrorEPC; + u32 r31; + TLBEntry tlb[32]; + TLBError tlbError; +}; +} \ No newline at end of file diff --git a/src/core/util.hpp b/src/core/util.hpp index a9ce5619..67c1663d 100644 --- a/src/core/util.hpp +++ b/src/core/util.hpp @@ -59,7 +59,7 @@ inline T ReadAccess(u8* data, u32 index) { static_assert(sizeof(T) != 2 && sizeof(T) != 4 && sizeof(T) != 8); T result = 0; memcpy(&result, &data[index], sizeof(T)); - return GetSwapFunc(result); + return GetSwapFunc(result); } template @@ -95,4 +95,16 @@ inline void SwapN64Rom(size_t size, u8* data) { panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n"); } } + +inline size_t NextPow2(size_t num) { + // Taken from "Bit Twiddling Hacks" by Sean Anderson: + // https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --num; + num |= num >> 1; + num |= num >> 2; + num |= num >> 4; + num |= num >> 8; + num |= num >> 16; + return num + 1; +} } diff --git a/src/frontend/qt/CMakeLists.txt b/src/frontend/qt/CMakeLists.txt index 1aa6e645..b158a30d 100644 --- a/src/frontend/qt/CMakeLists.txt +++ b/src/frontend/qt/CMakeLists.txt @@ -11,5 +11,5 @@ find_package(Qt5 COMPONENTS Widgets REQUIRED) add_executable(natsukashii-qt Frontend.hpp Frontend.cpp main.cpp) -target_include_directories(natsukashii-qt PRIVATE . ../../core ../../core/gb ../../core/n64 ../../../external) +target_include_directories(natsukashii-qt PRIVATE . ../../core ../../core/gb ../../core/n64) target_link_libraries(natsukashii-qt PRIVATE cores Qt5::Widgets) diff --git a/src/frontend/qt/Frontend.cpp b/src/frontend/qt/Frontend.cpp index debc1eab..c591dde4 100644 --- a/src/frontend/qt/Frontend.cpp +++ b/src/frontend/qt/Frontend.cpp @@ -1,7 +1,112 @@ #include +#include namespace natsukashii::frontend { -App::~App() {} - -App::App() {} +QVulkanWindowRenderer *VulkanWindow::createRenderer() { + return new VulkanRenderer(this); +} + +VulkanRenderer::VulkanRenderer(QVulkanWindow *w) + : m_window(w) { } + +void VulkanRenderer::initResources() { + m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device()); +} + +void VulkanRenderer::startNextFrame() { + VkClearColorValue clearColor = {{ 0.0f, 0.0f, 0.0f, 1.0f }}; + VkClearDepthStencilValue clearDS = { 1.0f, 0 }; + VkClearValue clearValues[2]; + memset(clearValues, 0, sizeof(clearValues)); + clearValues[0].color = clearColor; + clearValues[1].depthStencil = clearDS; + + VkRenderPassBeginInfo rpBeginInfo; + memset(&rpBeginInfo, 0, sizeof(rpBeginInfo)); + rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rpBeginInfo.renderPass = m_window->defaultRenderPass(); + rpBeginInfo.framebuffer = m_window->currentFramebuffer(); + const QSize sz = m_window->swapChainImageSize(); + rpBeginInfo.renderArea.extent.width = sz.width(); + rpBeginInfo.renderArea.extent.height = sz.height(); + rpBeginInfo.clearValueCount = 2; + rpBeginInfo.pClearValues = clearValues; + VkCommandBuffer cmdBuf = m_window->currentCommandBuffer(); + m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + // do thing + + m_devFuncs->vkCmdEndRenderPass(cmdBuf); + + m_window->frameReady(); + m_window->requestUpdate(); // render continuously, throttled by the presentation rate +} + +OpenGLWindow::OpenGLWindow(QWindow *parent) : QWindow(parent) { + setSurfaceType(QWindow::OpenGLSurface); +} + +void OpenGLWindow::render(QPainter *painter) { + Q_UNUSED(painter); +} + +void OpenGLWindow::initialize() {} + +void OpenGLWindow::render() { + if (!m_device) + m_device = new QOpenGLPaintDevice; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + m_device->setSize(size() * devicePixelRatio()); + m_device->setDevicePixelRatio(devicePixelRatio()); + + QPainter painter(m_device); + render(&painter); +} + +bool OpenGLWindow::event(QEvent *event) { + switch (event->type()) { + case QEvent::UpdateRequest: + renderNow(); + return true; + default: + return QWindow::event(event); + } +} + +void OpenGLWindow::exposeEvent(QExposeEvent *event) { + Q_UNUSED(event); + + if (isExposed()) + renderNow(); +} + +void OpenGLWindow::renderNow() { + if (!isExposed()) + return; + + bool needsInitialize = false; + + if (!m_context) { + m_context = new QOpenGLContext(this); + m_context->setFormat(requestedFormat()); + m_context->create(); + + needsInitialize = true; + } + + m_context->makeCurrent(this); + + if (needsInitialize) { + initializeOpenGLFunctions(); + initialize(); + } + + render(); + + m_context->swapBuffers(this); +} + + } diff --git a/src/frontend/qt/Frontend.hpp b/src/frontend/qt/Frontend.hpp index 7ee98a6c..8f4464e1 100644 --- a/src/frontend/qt/Frontend.hpp +++ b/src/frontend/qt/Frontend.hpp @@ -1,8 +1,63 @@ #pragma once +#include +#include +#include +#include +#include +#include + namespace natsukashii::frontend { -struct App { - ~App(); - App(); - void Run() {} +class VulkanRenderer : public QVulkanWindowRenderer { +public: + VulkanRenderer(QVulkanWindow *w); + + void initResources() override; + void initSwapChainResources() override; + void releaseSwapChainResources() override; + void releaseResources() override; + + void startNextFrame() override; + +private: + QVulkanWindow *m_window; + QVulkanDeviceFunctions *m_devFuncs; +}; + +class VulkanWindow : public QVulkanWindow { +public: + QVulkanWindowRenderer *createRenderer() override; +}; + +class OpenGLWindow : protected QOpenGLFunctions, public QWindow { + Q_OBJECT +public: + explicit OpenGLWindow(QWindow *parent = nullptr); + ~OpenGLWindow(); + + void render(QPainter *painter); + void render(); + + void initialize(); + + void setAnimating(bool animating); + +public slots: + void renderNow(); + +protected: + bool event(QEvent *event) override; + void exposeEvent(QExposeEvent *event) override; + +private: + QOpenGLContext *m_context = nullptr; + QOpenGLPaintDevice *m_device = nullptr; +}; + +class Window : public QWindow { +public: + Window() {} +private: + VulkanWindow vulkanWindow; + OpenGLWindow openGlWindow; }; } diff --git a/src/frontend/qt/main.cpp b/src/frontend/qt/main.cpp index 37e04d8c..54d60fb1 100644 --- a/src/frontend/qt/main.cpp +++ b/src/frontend/qt/main.cpp @@ -1,8 +1,10 @@ #include -#include +#include int main(int argc, char* argv[]) { - natsukashii::frontend::App app; - app.Run(); - return 0; + QGuiApplication app(argc, argv); + natsukashii::frontend::Window window; + window.show(); + + return app.exec(); } diff --git a/src/frontend/sdl/CMakeLists.txt b/src/frontend/sdl/CMakeLists.txt index b93f1443..0a491789 100644 --- a/src/frontend/sdl/CMakeLists.txt +++ b/src/frontend/sdl/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(SDL2 REQUIRED) -add_executable(natsukashii-sdl Frontend.cpp Frontend.hpp main.cpp) +add_executable(natsukashii-sdl Frontend.cpp Frontend.hpp main.cpp ParallelRDPWrapper.cpp ParallelRDPWrapper.hpp) include(FetchContent) FetchContent_Declare( @@ -15,5 +15,7 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(argparse) -target_include_directories(natsukashii-sdl PRIVATE . ../../core ../../core/gb ../../core/n64 ../../../external) -target_link_libraries(natsukashii-sdl PRIVATE cores argparse::argparse SDL2) +add_subdirectory(../../../external temp) + +target_include_directories(natsukashii-sdl PRIVATE . ../../core ../../core/gb ../../core/n64 ../../core/n64/core ../../core/n64/core/cpu/registers) +target_link_libraries(natsukashii-sdl PRIVATE cores argparse::argparse SDL2 parallel-rdp) diff --git a/src/frontend/sdl/Frontend.cpp b/src/frontend/sdl/Frontend.cpp index 5722af61..f7a305fe 100644 --- a/src/frontend/sdl/Frontend.cpp +++ b/src/frontend/sdl/Frontend.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace natsukashii::frontend { using namespace natsukashii; @@ -11,14 +12,20 @@ App::~App() { } App::App(const std::string& rom, const std::string& selectedCore) { - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); - window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN); - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - id = SDL_GetWindowID(window); + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_VIDEO_VULKAN); if(selectedCore == "gb") { + window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_RESIZABLE); + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + SDL_RenderSetLogicalSize(renderer, 160, 144); + id = SDL_GetWindowID(window); core = std::make_unique(rom); } else if(selectedCore == "n64") { + window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_RESIZABLE | SDL_WINDOW_VULKAN); + id = SDL_GetWindowID(window); + if(volkInitialize() != VK_SUCCESS) { + util::panic("Failed to initialize Volk\n"); + } core = std::make_unique(rom); } else { util::panic("Unimplemented core!"); @@ -26,12 +33,9 @@ App::App(const std::string& rom, const std::string& selectedCore) { } void App::Run() { - while(!quit) { + while(!core->ShouldQuit()) { core->Run(); - - SDL_Event event; - SDL_PollEvent(&event); - quit = event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == id; + core->PollInputs(id); } } } diff --git a/src/frontend/sdl/Frontend.hpp b/src/frontend/sdl/Frontend.hpp index 0379fb76..f7bb03d1 100644 --- a/src/frontend/sdl/Frontend.hpp +++ b/src/frontend/sdl/Frontend.hpp @@ -1,6 +1,7 @@ #pragma once #define SDL_MAIN_HANDLED #include +#include #include #include #include @@ -8,6 +9,7 @@ namespace natsukashii::frontend { using namespace natsukashii::core; + struct App { ~App(); App(const std::string&, const std::string&); @@ -15,8 +17,7 @@ struct App { private: SDL_Window *window = nullptr; SDL_Renderer *renderer = nullptr; - Uint32 id; - bool quit = false; + u32 id; std::unique_ptr core; }; } diff --git a/src/frontend/sdl/ParallelRDPWrapper.cpp b/src/frontend/sdl/ParallelRDPWrapper.cpp new file mode 100644 index 00000000..783311dd --- /dev/null +++ b/src/frontend/sdl/ParallelRDPWrapper.cpp @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace Vulkan; +using namespace natsukashii; +using std::unique_ptr; +using RDP::CommandProcessor; +using RDP::CommandProcessorFlags; +using RDP::VIRegister; + +static CommandProcessor* command_processor; +static WSI* wsi; + +std::vector acquire_semaphore; + +VkQueue GetGraphicsQueue() { + return wsi->get_context().get_graphics_queue(); +} + +VkInstance GetVkInstance() { + return wsi->get_context().get_instance(); +} + +VkPhysicalDevice GetVkPhysicalDevice() { + return wsi->get_device().get_physical_device(); +} + +VkDevice GetVkDevice() { + return wsi->get_device().get_device(); +} + +uint32_t GetVkGraphicsQueueFamily() { + return wsi->get_context().get_graphics_queue_family(); +} + +VkFormat GetVkFormat() { + return wsi->get_device().get_swapchain_view().get_format(); +} + +CommandBufferHandle requested_command_buffer; + +VkCommandBuffer GetVkCommandBuffer() { + requested_command_buffer = wsi->get_device().request_command_buffer(); + return requested_command_buffer->get_command_buffer(); +} + +void SubmitRequestedVkCommandBuffer() { + wsi->get_device().submit(requested_command_buffer); +} + +bool IsFramerateUnlocked() { + return wsi->get_present_mode() != PresentMode::SyncToVBlank; +} + +void SetFramerateUnlocked(bool unlocked) { + if (unlocked) { + wsi->set_present_mode(PresentMode::UnlockedForceTearing); + } else { + wsi->set_present_mode(PresentMode::SyncToVBlank); + } +} + +class SDLWSIPlatform : public Vulkan::WSIPlatform { +public: + SDLWSIPlatform() = default; + + std::vector get_instance_extensions() override { + const char* extensions[64]; + unsigned int num_extensions = 64; + + if (!SDL_Vulkan_GetInstanceExtensions(window, &num_extensions, extensions)) { + util::panic("SDL_Vulkan_GetInstanceExtensions failed: %s", SDL_GetError()); + } + auto vec = std::vector(); + + for (unsigned int i = 0; i < num_extensions; i++) { + vec.push_back(extensions[i]); + } + + return vec; + } + + VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) override { + VkSurfaceKHR vk_surface; + if (!SDL_Vulkan_CreateSurface(window, instance, &vk_surface)) { + util::panic("Failed to create Vulkan window surface: %s", SDL_GetError()); + } + return vk_surface; + } + + uint32_t get_surface_width() override { + return N64_SCREEN_X * SCREEN_SCALE; + } + + uint32_t get_surface_height() override { + return N64_SCREEN_Y * SCREEN_SCALE; + } + + bool alive(Vulkan::WSI &wsi) override { + return true; + } + + void poll_input() override { + n64_poll_input(); + } + + void event_frame_tick(double frame, double elapsed) override { + n64_render_screen(); + } +}; + +Program* fullscreen_quad_program; + +void LoadParallelRDP(const u8* rdram) { + wsi = new WSI(); + wsi->set_backbuffer_srgb(false); + wsi->set_platform(new SDLWSIPlatform()); + Context::SystemHandles handles; + if (!wsi->init(1, handles)) { + util::panic("Failed to initialize WSI!"); + } + + ResourceLayout vert_layout; + ResourceLayout frag_layout; + + vert_layout.input_mask = 1; + vert_layout.output_mask = 1; + + frag_layout.input_mask = 1; + frag_layout.output_mask = 1; + frag_layout.spec_constant_mask = 1; + frag_layout.push_constant_size = 4 * sizeof(float); + + frag_layout.sets[0].sampled_image_mask = 1; + frag_layout.sets[0].fp_mask = 1; + frag_layout.sets[0].array_size[0] = 1; + + fullscreen_quad_program = wsi->get_device().request_program(fullscreen_quad_vert, sizeof(fullscreen_quad_vert), fullscreen_quad_frag, sizeof(fullscreen_quad_frag), &vert_layout, &frag_layout); + + auto aligned_rdram = reinterpret_cast(rdram); + uintptr_t offset = 0; + + if (wsi->get_device().get_device_features().supports_external_memory_host) + { + size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment; + offset = aligned_rdram & (align - 1); + aligned_rdram -= offset; + } + + CommandProcessorFlags flags = 1 << 1; // TODO configurable scaling + + command_processor = new CommandProcessor(wsi->get_device(), reinterpret_cast(aligned_rdram), + offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags); + + if (!command_processor->device_is_supported()) { + util::panic("This device probably does not support 8/16-bit storage. Make sure you're using up-to-date drivers!"); + } +} + +void draw_fullscreen_textured_quad(Util::IntrusivePtr image, Util::IntrusivePtr cmd) { + cmd->set_texture(0, 0, image->get_view(), Vulkan::StockSampler::LinearClamp); + cmd->set_program(fullscreen_quad_program); + cmd->set_quad_state(); + auto data = static_cast(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float))); + *data++ = -1.0f; + *data++ = -3.0f; + *data++ = -1.0f; + *data++ = +1.0f; + *data++ = +3.0f; + *data++ = +1.0f; + + int sdlWinWidth, sdlWinHeight; + SDL_GetWindowSize(window, &sdlWinWidth, &sdlWinHeight); + + float zoom = std::min( + (float)sdlWinWidth / wsi->get_platform().get_surface_width(), + (float)sdlWinHeight / wsi->get_platform().get_surface_height()); + + float width = (wsi->get_platform().get_surface_width() / (float)sdlWinWidth) * zoom; + float height = (wsi->get_platform().get_surface_height() / (float)sdlWinHeight) * zoom; + + float uniform_data[] = { + // Size + width, height, + // Offset + (1.0f - width) * 0.5f, + (1.0f - height) * 0.5f}; + + cmd->push_constants(uniform_data, 0, sizeof(uniform_data)); + + cmd->set_vertex_attrib(0, 0, VK_FORMAT_R32G32_SFLOAT, 0); + cmd->set_depth_test(false, false); + cmd->set_depth_compare(VK_COMPARE_OP_ALWAYS); + cmd->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + cmd->draw(3, 1); +} + +void update_screen(Util::IntrusivePtr image) { + wsi->begin_frame(); + + if (!image) { + auto info = Vulkan::ImageCreateInfo::immutable_2d_image(N64_SCREEN_X * SCREEN_SCALE, N64_SCREEN_Y * SCREEN_SCALE, VK_FORMAT_R8G8B8A8_UNORM); + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + info.misc = IMAGE_MISC_MUTABLE_SRGB_BIT; + info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED; + image = wsi->get_device().create_image(info); + + auto cmd = wsi->get_device().request_command_buffer(); + + cmd->image_barrier(*image, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); + cmd->clear_image(*image, {}); + cmd->image_barrier(*image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); + wsi->get_device().submit(cmd); + } + + Util::IntrusivePtr cmd = wsi->get_device().request_command_buffer(); + + cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly)); + draw_fullscreen_textured_quad(image, cmd); + ImGui_ImplVulkan_RenderDrawData(imgui_frame(), cmd->get_command_buffer()); + cmd->end_render_pass(); + wsi->get_device().submit(cmd); + wsi->end_frame(); +} + +void update_screen_parallel_rdp() { + if (unlikely(!command_processor)) { + logfatal("Update screen without an initialized command processor"); + } + + command_processor->set_vi_register(VIRegister::Control, n64sys.vi.status.raw); + command_processor->set_vi_register(VIRegister::Origin, n64sys.vi.vi_origin); + command_processor->set_vi_register(VIRegister::Width, n64sys.vi.vi_width); + command_processor->set_vi_register(VIRegister::Intr, n64sys.vi.vi_v_intr); + command_processor->set_vi_register(VIRegister::VCurrentLine, n64sys.vi.v_current); + command_processor->set_vi_register(VIRegister::Timing, n64sys.vi.vi_burst.raw); + command_processor->set_vi_register(VIRegister::VSync, n64sys.vi.vsync); + command_processor->set_vi_register(VIRegister::HSync, n64sys.vi.hsync); + command_processor->set_vi_register(VIRegister::Leap, n64sys.vi.leap); + command_processor->set_vi_register(VIRegister::HStart, n64sys.vi.hstart.raw); + command_processor->set_vi_register(VIRegister::VStart, n64sys.vi.vstart.raw); + command_processor->set_vi_register(VIRegister::VBurst, n64sys.vi.vburst); + command_processor->set_vi_register(VIRegister::XScale, n64sys.vi.xscale.raw); + command_processor->set_vi_register(VIRegister::YScale, n64sys.vi.yscale.raw); + + RDP::ScanoutOptions opts; + opts.persist_frame_on_invalid_input = true; + opts.vi.aa = true; + opts.vi.scale = true; + opts.vi.dither_filter = true; + opts.vi.divot_filter = true; + opts.vi.gamma_dither = true; + opts.downscale_steps = true; + opts.crop_overscan_pixels = true; + Util::IntrusivePtr image = command_processor->scanout(opts); + update_screen(image); + command_processor->begin_frame_context(); +} + +void update_screen_parallel_rdp_no_game() { + update_screen(static_cast>(nullptr)); +} + +void parallel_rdp_enqueue_command(int command_length, word* buffer) { + command_processor->enqueue_command(command_length, buffer); +} + +void parallel_rdp_on_full_sync() { + command_processor->wait_for_timeline(command_processor->signal_timeline()); +} \ No newline at end of file diff --git a/src/frontend/sdl/ParallelRDPWrapper.hpp b/src/frontend/sdl/ParallelRDPWrapper.hpp new file mode 100644 index 00000000..056d5781 --- /dev/null +++ b/src/frontend/sdl/ParallelRDPWrapper.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +#include + +VkQueue GetGraphicsQueue(); +VkInstance GetVkInstance(); +VkPhysicalDevice GetVkPhysicalDevice(); +VkDevice GetVkDevice(); +uint32_t GetVkGraphicsQueueFamily(); +VkFormat GetVkFormat(); +VkCommandBuffer GetVkCommandBuffer(); +void SubmitRequestedVkCommandBuffer(); +void LoadParallelRdp(); +void UpdateScreenParallelRdp(); +void ParallelRdpEnqueueCommand(int command_length, u32* buffer); +void ParallelRdpOnFullSync(); +void UpdateScreenParallelRdpNoGame(); +bool IsFramerateUnlocked(); +void SetFramerateUnlocked(bool unlocked); \ No newline at end of file