From 722a0e98c0d0c2aac6622e2cae563e4e8633c672 Mon Sep 17 00:00:00 2001 From: SimoZ64 Date: Thu, 22 May 2025 00:30:33 +0200 Subject: [PATCH] Some progress --- .github/workflows/build.yml | 60 ------- external/parallel-rdp/ParallelRDPWrapper.cpp | 64 ++++---- external/parallel-rdp/ParallelRDPWrapper.hpp | 6 +- src/frontend/CMakeLists.txt | 2 + src/frontend/EmuThread.cpp | 90 +++++++---- src/frontend/EmuThread.hpp | 6 +- src/frontend/ImGuiImpl/GUI.hpp | 157 +++++++++++++++++++ src/frontend/ImGuiImpl/StatusBar.hpp | 2 + src/frontend/InputSettings.cpp | 2 + src/frontend/KaizenGui.cpp | 71 ++++----- src/frontend/KaizenGui.hpp | 8 +- src/frontend/NativeWindow.hpp | 6 + src/frontend/RenderWidget.cpp | 9 +- src/frontend/RenderWidget.hpp | 12 +- 14 files changed, 317 insertions(+), 178 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 src/frontend/ImGuiImpl/GUI.hpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index d465904c..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: build -on: - push: - branches: - - master -jobs: - build-linux: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@master - with: - submodules: recursive - - name: Install dependencies - run: | - sudo add-apt-repository universe - sudo apt-get update -qq - sudo apt-get install -y clang build-essential libgtk-3-dev git ninja-build qt6-base-dev - sudo apt-get install -y vulkan-tools libvulkan1 libvulkan-dev vulkan-utility-libraries-dev spirv-tools - - name: Build Kaizen - run: | - cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -B build -DCMAKE_BUILD_TYPE=Release - cmake --build build --config Release - - name: Collect artifacts - run: | - mkdir upload - cp -r build/src/frontend/{kaizen-qt,resources} upload - - name: Upload artifacts - uses: actions/upload-artifact@master - with: - name: kaizen-linux - path: upload - if-no-files-found: error - build-windows: - runs-on: windows-latest - steps: - - uses: actions/checkout@master - with: - submodules: recursive - - name: Setup Qt - uses: jurplel/install-qt-action@v3 - with: - arch: win64_msvc2019_64 - version: 6.6.* - - name: Build Kaizen - run: | - cmake -B build -T clangcl -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release - cmake --build build --config Release - - name: Collect artifacts - run: | - mkdir upload - cp build/src/frontend/Release/kaizen-qt.exe upload - mkdir upload/resources - cp resources/* upload/resources - windeployqt --dir upload upload/kaizen-qt.exe - - name: Upload artifacts - uses: actions/upload-artifact@master - with: - name: kaizen-windows - path: upload - if-no-files-found: error \ No newline at end of file diff --git a/external/parallel-rdp/ParallelRDPWrapper.cpp b/external/parallel-rdp/ParallelRDPWrapper.cpp index 7da8359e..91830c65 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.cpp +++ b/external/parallel-rdp/ParallelRDPWrapper.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace Vulkan; using namespace RDP; @@ -156,7 +157,7 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr image, cmd->draw(3, 1); } -void ParallelRDP::UpdateScreen(Util::IntrusivePtr image) const { +void ParallelRDP::UpdateScreen(Util::IntrusivePtr image, KaizenGui& kaizenGui) const { wsi->begin_frame(); if (!image) { @@ -182,39 +183,44 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr image) const { cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly)); DrawFullscreenTexturedQuad(image, cmd); - + kaizenGui.RenderUI(); cmd->end_render_pass(); wsi->get_device().submit(cmd); wsi->end_frame(); } -void ParallelRDP::UpdateScreen(const n64::VI &vi) const { - command_processor->set_vi_register(VIRegister::Control, vi.status.raw); - command_processor->set_vi_register(VIRegister::Origin, vi.origin); - command_processor->set_vi_register(VIRegister::Width, vi.width); - command_processor->set_vi_register(VIRegister::Intr, vi.intr); - command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current); - command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw); - command_processor->set_vi_register(VIRegister::VSync, vi.vsync); - command_processor->set_vi_register(VIRegister::HSync, vi.hsync); - command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw); - command_processor->set_vi_register(VIRegister::HStart, vi.hstart.raw); - command_processor->set_vi_register(VIRegister::VStart, vi.vstart.raw); - command_processor->set_vi_register(VIRegister::VBurst, vi.vburst); - command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw); - command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw); - 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); - UpdateScreen(image); - command_processor->begin_frame_context(); +void ParallelRDP::UpdateScreen(const n64::VI &vi, KaizenGui& kaizenGui, bool playing) const { + if(playing) { + command_processor->set_vi_register(VIRegister::Control, vi.status.raw); + command_processor->set_vi_register(VIRegister::Origin, vi.origin); + command_processor->set_vi_register(VIRegister::Width, vi.width); + command_processor->set_vi_register(VIRegister::Intr, vi.intr); + command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current); + command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw); + command_processor->set_vi_register(VIRegister::VSync, vi.vsync); + command_processor->set_vi_register(VIRegister::HSync, vi.hsync); + command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw); + command_processor->set_vi_register(VIRegister::HStart, vi.hstart.raw); + command_processor->set_vi_register(VIRegister::VStart, vi.vstart.raw); + command_processor->set_vi_register(VIRegister::VBurst, vi.vburst); + command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw); + command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw); + 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); + UpdateScreen(image, kaizenGui); + command_processor->begin_frame_context(); + return; + } + + UpdateScreen(static_cast>(nullptr), kaizenGui); } void ParallelRDP::EnqueueCommand(int command_length, const u32 *buffer) const { diff --git a/external/parallel-rdp/ParallelRDPWrapper.hpp b/external/parallel-rdp/ParallelRDPWrapper.hpp index 6c1968af..238eba56 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.hpp +++ b/external/parallel-rdp/ParallelRDPWrapper.hpp @@ -7,6 +7,8 @@ namespace n64 { struct VI; } +class KaizenGui; + class ParallelRDP { public: class WindowInfo { @@ -23,7 +25,7 @@ public: const std::shared_ptr &, const u8 *); ParallelRDP() = default; - void UpdateScreen(const n64::VI &) const; + void UpdateScreen(const n64::VI &, KaizenGui&, bool = true) const; void EnqueueCommand(int, const u32 *) const; void OnFullSync() const; bool IsFramerateUnlocked() const; @@ -36,5 +38,5 @@ public: private: void LoadWSIPlatform(const std::shared_ptr &, const std::shared_ptr &); void DrawFullscreenTexturedQuad(Util::IntrusivePtr, Util::IntrusivePtr) const; - void UpdateScreen(Util::IntrusivePtr) const; + void UpdateScreen(Util::IntrusivePtr, KaizenGui&) const; }; diff --git a/src/frontend/CMakeLists.txt b/src/frontend/CMakeLists.txt index 10d58a51..b73cb9d5 100644 --- a/src/frontend/CMakeLists.txt +++ b/src/frontend/CMakeLists.txt @@ -6,6 +6,8 @@ if (WIN32) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) endif () +add_compile_definitions(VK_NO_PROTOTYPES) + if(APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0) enable_language(OBJC) diff --git a/src/frontend/EmuThread.cpp b/src/frontend/EmuThread.cpp index 8cfe0105..3461c33d 100644 --- a/src/frontend/EmuThread.cpp +++ b/src/frontend/EmuThread.cpp @@ -1,15 +1,13 @@ #include #include +#include EmuThread::EmuThread(const std::shared_ptr &core, double &fps, RenderWidget &renderWidget, - SettingsWindow &settings) noexcept : - renderWidget(renderWidget), core(core), settings(settings), fps(fps) {} + SettingsWindow &settings, KaizenGui& kaizenGui) noexcept : + renderWidget(renderWidget), core(core), settings(settings), fps(fps) { -void EmuThread::start() noexcept { - thread = std::thread([this]() { + thread = std::thread([&]() { isRunning = true; - core->parallel.Init(renderWidget.wsiPlatform, renderWidget.windowInfo, - core->cpu->GetMem().GetRDRAMPtr()); auto lastSample = std::chrono::high_resolution_clock::now(); auto avgFps = 16.667; @@ -19,32 +17,60 @@ void EmuThread::start() noexcept { fps = 1000.0 / avgFps; while (!interruptionRequested) { - const auto startFrameTime = std::chrono::high_resolution_clock::now(); - if (!core->pause) { - core->Run(settings.getVolumeL(), settings.getVolumeR()); - } - - if (core->render) { - core->parallel.UpdateScreen(core->cpu->GetMem().mmio.vi); - } - - 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; - continue; + if(!started) { + const auto startFrameTime = std::chrono::high_resolution_clock::now(); + core->parallel.UpdateScreen(core->cpu->GetMem().mmio.vi, kaizenGui, false); + 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; + continue; + } + lastSample = endFrameTime; + avgFps /= sampledFps; + sampledFps = 0; + fps = 1000.0 / avgFps; + } + + continue; + } + + if(started) { + started = false; + const auto startFrameTime = std::chrono::high_resolution_clock::now(); + if (!core->pause) { + core->Run(settings.getVolumeL(), settings.getVolumeR()); + } + + if (core->render) { + core->parallel.UpdateScreen(core->cpu->GetMem().mmio.vi, kaizenGui); + } + + 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; + continue; + } + lastSample = endFrameTime; + avgFps /= sampledFps; + sampledFps = 0; + fps = 1000.0 / avgFps; } - lastSample = endFrameTime; - avgFps /= sampledFps; - sampledFps = 0; - fps = 1000.0 / avgFps; } } SetRender(false); @@ -53,6 +79,8 @@ void EmuThread::start() noexcept { }); } +void EmuThread::start() noexcept { started = true; } + void EmuThread::TogglePause() const noexcept { core->TogglePause(); Util::RPC::GetInstance().Update(core->pause ? Util::RPC::Paused : Util::RPC::GetInstance().GetState(), diff --git a/src/frontend/EmuThread.hpp b/src/frontend/EmuThread.hpp index 9b0193b0..2f248292 100644 --- a/src/frontend/EmuThread.hpp +++ b/src/frontend/EmuThread.hpp @@ -8,11 +8,13 @@ namespace n64 { struct Core; } +class KaizenGui; + class EmuThread final { RenderWidget &renderWidget; - + bool started = false; public: - explicit EmuThread(const std::shared_ptr &, double &, RenderWidget &, SettingsWindow &) noexcept; + explicit EmuThread(const std::shared_ptr &, double &, RenderWidget &, SettingsWindow &, KaizenGui&) noexcept; void start() noexcept; void TogglePause() const noexcept; diff --git a/src/frontend/ImGuiImpl/GUI.hpp b/src/frontend/ImGuiImpl/GUI.hpp new file mode 100644 index 00000000..bf93ccb6 --- /dev/null +++ b/src/frontend/ImGuiImpl/GUI.hpp @@ -0,0 +1,157 @@ +#pragma once +#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES +#include +#include +#include +#include +#include + +class KaizenGui; + +namespace gui { + static VkAllocationCallbacks* g_Allocator = NULL; + static VkInstance g_Instance = VK_NULL_HANDLE; + static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; + static VkDevice g_Device = VK_NULL_HANDLE; + static uint32_t g_QueueFamily = (uint32_t)-1; + static VkQueue g_Queue = VK_NULL_HANDLE; + static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; + static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; + + static ImGui_ImplVulkanH_Window g_MainWindowData; + static uint32_t g_MinImageCount = 2; + + inline std::shared_ptr g_Wsi; + + static void CheckVkResult(VkResult err) { + if (err == 0) + return; + Util::error("[vulkan] VkResult = {}", (int) err); + if (err < 0) + abort(); + } + + inline void Initialize(const std::shared_ptr& wsi, SDL_Window* nativeWindow) { + VkResult err; + g_Wsi = wsi; + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + g_Instance = wsi->get_context().get_instance(); + g_PhysicalDevice = wsi->get_device().get_physical_device(); + g_Device = wsi->get_device().get_device(); + g_QueueFamily = wsi->get_context().get_queue_info().family_indices[Vulkan::QUEUE_INDEX_GRAPHICS]; + g_Queue = wsi->get_context().get_queue_info().queues[Vulkan::QUEUE_INDEX_GRAPHICS]; + g_PipelineCache = nullptr; + g_DescriptorPool = nullptr; + g_Allocator = nullptr; + g_MinImageCount = 2; + + + // Create Descriptor Pool + { + VkDescriptorPoolSize pool_sizes[] = + { + { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } + }; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes); + pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.pPoolSizes = pool_sizes; + err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); + CheckVkResult(err); + } + + // Create the Render Pass + VkRenderPass renderPass; + { + VkAttachmentDescription attachment = {}; + attachment.format = wsi->get_device().get_swapchain_view().get_format(); + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference color_attachment = {}; + color_attachment.attachment = 0; + color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment; + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkRenderPassCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + info.attachmentCount = 1; + info.pAttachments = &attachment; + info.subpassCount = 1; + info.pSubpasses = &subpass; + info.dependencyCount = 1; + info.pDependencies = &dependency; + err = vkCreateRenderPass(g_Device, &info, g_Allocator, &renderPass); + CheckVkResult(err); + } + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForVulkan(nativeWindow); + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = g_Instance; + init_info.PhysicalDevice = g_PhysicalDevice; + init_info.Device = g_Device; + init_info.QueueFamily = g_QueueFamily; + init_info.Queue = g_Queue; + init_info.PipelineCache = g_PipelineCache; + init_info.DescriptorPool = g_DescriptorPool; + init_info.Allocator = g_Allocator; + init_info.MinImageCount = g_MinImageCount; + init_info.ImageCount = 2; + init_info.CheckVkResultFn = CheckVkResult; + init_info.RenderPass = renderPass; + + ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_3, [](const char *function_name, void *vulkan_instance) { + return vkGetInstanceProcAddr((reinterpret_cast(vulkan_instance)), function_name); + }, g_Instance); + + ImGui_ImplVulkan_Init(&init_info); + } + + inline void StartFrame() { + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + } + + inline void EndFrame() { + ImGui::Render(); + auto cmd = g_Wsi->get_device().request_command_buffer(); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd->get_command_buffer()); + } +} \ No newline at end of file diff --git a/src/frontend/ImGuiImpl/StatusBar.hpp b/src/frontend/ImGuiImpl/StatusBar.hpp index c8431e87..20ef8af2 100644 --- a/src/frontend/ImGuiImpl/StatusBar.hpp +++ b/src/frontend/ImGuiImpl/StatusBar.hpp @@ -23,6 +23,8 @@ struct StatusBar { if(exec) exec(); ImGui::End(); + + return true; } private: std::function exec = nullptr; diff --git a/src/frontend/InputSettings.cpp b/src/frontend/InputSettings.cpp index 66a1f6ee..5eff9b91 100644 --- a/src/frontend/InputSettings.cpp +++ b/src/frontend/InputSettings.cpp @@ -38,6 +38,8 @@ bool InputSettings::render() { i++; } + + return true; } std::array InputSettings::GetMappedKeys() { diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index 25d6ba99..1d2c4d96 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -2,20 +2,12 @@ #include #include #include -#include -#include -#include +#include -static void check_vk_result(VkResult err) -{ - if (err == 0) - return; - fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); - if (err < 0) - abort(); -} - -KaizenGui::KaizenGui() noexcept : window("Kaizen", 1280, 720), core(std::make_shared()), vulkanWidget(core, window.getHandle()), emuThread(core, fpsCounter, vulkanWidget, settingsWindow) { +KaizenGui::KaizenGui() noexcept : window("Kaizen", 1280, 720), core(std::make_shared()), vulkanWidget(core, window.getHandle()), emuThread(core, fpsCounter, vulkanWidget, settingsWindow, *this) { + core->parallel.Init(vulkanWidget.wsiPlatform, vulkanWidget.windowInfo, core->cpu->GetMem().GetRDRAMPtr()); + gui::Initialize(core->parallel.wsi, window.getHandle()); + emuExitFunc = [&]() { quit = true; if (emuThread.isRunning) { @@ -28,7 +20,7 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 1280, 720), core(std::make_sh 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 trademarks of Nintendo Co., Ltd."); + ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); if(ImGui::Button("OK")) { about.setOpened(false); } @@ -55,24 +47,34 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 1280, 720), core(std::make_sh } }); - menuBar.addMenu({"Emulation", - { - actionPause, - actionStop, - actionReset, - {"Settings", [&]() { - settingsWindow.render(); - }}, - } - }); + menuBar.addMenu({ + "Emulation", { + actionPause, + actionStop, + actionReset, + {"Settings", [&]() { + settingsWindow.render(); + }}, + }}); - menuBar.addMenu({"Help", - { - {"About", [&]() { - about.setOpened(true); - }}, - } - }); + menuBar.addMenu({ + "Help", { + {"About", [&]() { + about.setOpened(true); + }}, + }}); +} + +void KaizenGui::RenderUI() { + gui::StartFrame(); + + menuBar.render(); + + about.render(); + + statusBar.render(); + + gui::EndFrame(); } void KaizenGui::LoadROM(const std::string &path) noexcept { @@ -88,6 +90,7 @@ void KaizenGui::LoadROM(const std::string &path) noexcept { void KaizenGui::handleEvents() { SDL_Event e; while(SDL_PollEvent(&e)) { + ImGui_ImplSDL3_ProcessEvent(&e); switch(e.type) { case SDL_EVENT_QUIT: emuExitFunc(); @@ -99,12 +102,6 @@ void KaizenGui::handleEvents() { int KaizenGui::run() { while(!quit) { handleEvents(); - - menuBar.render(); - - about.render(); - - statusBar.render(); } return 0; diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index 0d7026ac..f15dbb0f 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -1,6 +1,6 @@ #pragma once -#include #include +#include #include #include #include @@ -18,6 +18,9 @@ public: std::shared_ptr core; RenderWidget vulkanWidget; EmuThread emuThread; + gui::PopupWindow about{"About Kaizen"}; + gui::StatusBar statusBar{}; + void RenderUI(); int run(); void LoadTAS(const std::string &path) const noexcept; @@ -26,7 +29,4 @@ private: bool quit = false; void handleEvents(); std::function emuExitFunc; - gui::PopupWindow about{"About Kaizen"}; - gui::StatusBar statusBar{}; - void InitImGui(); }; diff --git a/src/frontend/NativeWindow.hpp b/src/frontend/NativeWindow.hpp index d4213d6c..000c11e6 100644 --- a/src/frontend/NativeWindow.hpp +++ b/src/frontend/NativeWindow.hpp @@ -2,12 +2,18 @@ #include #include #include +#include +#include namespace gui { struct NativeWindow { NativeWindow(const std::string& title, int w, int h, int posX = SDL_WINDOWPOS_CENTERED, int posY = SDL_WINDOWPOS_CENTERED) { SDL_Init(SDL_INIT_VIDEO); window = SDL_CreateWindow(title.c_str(), w, h, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); + + if(volkInitialize() != VK_SUCCESS) { + Util::panic("Failed to initialize Volk!"); + } } ~NativeWindow() { diff --git a/src/frontend/RenderWidget.cpp b/src/frontend/RenderWidget.cpp index 0009b508..a5455172 100644 --- a/src/frontend/RenderWidget.cpp +++ b/src/frontend/RenderWidget.cpp @@ -4,12 +4,9 @@ #include #include -RenderWidget::RenderWidget(const std::shared_ptr &core, InputSettings& inputSettings, SDL_Window* window) : inputSettings(inputSettings) { - if (!Vulkan::Context::init_loader(nullptr)) { - Util::panic("Could not initialize Vulkan ICD"); - } - - wsiPlatform = std::make_shared(core, inputSettings, window); +RenderWidget::RenderWidget(const std::shared_ptr &core, SDL_Window* window) { + wsiPlatform = std::make_shared(core, window); + windowInfo = std::make_shared(window); } void SDLWSIPlatform::poll_input() { diff --git a/src/frontend/RenderWidget.hpp b/src/frontend/RenderWidget.hpp index 515f42cf..dc37c821 100644 --- a/src/frontend/RenderWidget.hpp +++ b/src/frontend/RenderWidget.hpp @@ -11,20 +11,20 @@ struct Core; class SDLParallelRdpWindowInfo final : public ParallelRDP::WindowInfo { public: - explicit SDLParallelRdpWindowInfo(const std::shared_ptr window) : window(window) {} + explicit SDLParallelRdpWindowInfo(SDL_Window* window) : window(window) {} CoordinatePair get_window_size() override { int w,h; - SDL_GetWindowSizeInPixels(window.get(), &w, &h); + SDL_GetWindowSizeInPixels(window, &w, &h); return CoordinatePair{static_cast(w), static_cast(h)}; } private: - std::shared_ptr window{}; + SDL_Window* window{}; }; class SDLWSIPlatform final : public Vulkan::WSIPlatform { public: - explicit SDLWSIPlatform(const std::shared_ptr &core, InputSettings& inputSettings, SDL_Window* window) : window(window), inputSettings(inputSettings), core(core) {} + explicit SDLWSIPlatform(const std::shared_ptr &core, SDL_Window* window) : window(window), core(core) {} std::vector get_instance_extensions() override { auto vec = std::vector(); @@ -69,16 +69,14 @@ public: private: std::shared_ptr core; SDL_Gamepad *gamepad{}; - InputSettings& inputSettings; bool gamepadConnected = false; bool canPollEvents = true; }; class RenderWidget final { public: - explicit RenderWidget(const std::shared_ptr &, InputSettings&, SDL_Window*); + explicit RenderWidget(const std::shared_ptr &, SDL_Window*); std::shared_ptr windowInfo; std::shared_ptr wsiPlatform; - InputSettings& inputSettings; };