Some progress

This commit is contained in:
SimoZ64
2025-05-22 00:30:33 +02:00
parent 8fa341bf72
commit 722a0e98c0
14 changed files with 317 additions and 178 deletions

View File

@@ -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

View File

@@ -5,6 +5,7 @@
#include <core/mmio/VI.hpp>
#include <resources/vert.spv.h>
#include <resources/frag.spv.h>
#include <KaizenGui.hpp>
using namespace Vulkan;
using namespace RDP;
@@ -156,7 +157,7 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
cmd->draw(3, 1);
}
void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image, KaizenGui& kaizenGui) const {
wsi->begin_frame();
if (!image) {
@@ -182,39 +183,44 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> 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> 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> image = command_processor->scanout(opts);
UpdateScreen(image, kaizenGui);
command_processor->begin_frame_context();
return;
}
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr), kaizenGui);
}
void ParallelRDP::EnqueueCommand(int command_length, const u32 *buffer) const {

View File

@@ -7,6 +7,8 @@ namespace n64 {
struct VI;
}
class KaizenGui;
class ParallelRDP {
public:
class WindowInfo {
@@ -23,7 +25,7 @@ public:
const std::shared_ptr<WindowInfo> &, 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<Vulkan::WSIPlatform> &, const std::shared_ptr<WindowInfo> &);
void DrawFullscreenTexturedQuad(Util::IntrusivePtr<Vulkan::Image>, Util::IntrusivePtr<Vulkan::CommandBuffer>) const;
void UpdateScreen(Util::IntrusivePtr<Vulkan::Image>) const;
void UpdateScreen(Util::IntrusivePtr<Vulkan::Image>, KaizenGui&) const;
};

View File

@@ -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)

View File

@@ -1,15 +1,13 @@
#include <Core.hpp>
#include <EmuThread.hpp>
#include <KaizenGui.hpp>
EmuThread::EmuThread(const std::shared_ptr<n64::Core> &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<double>(endFrameTime - startFrameTime) / 1ms;
avgFps += frameTimeMs;
sampledFps++;
if (const auto elapsedSinceLastSample = std::chrono::duration<double>(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<double>(endFrameTime - startFrameTime) / 1ms;
avgFps += frameTimeMs;
sampledFps++;
if (const auto elapsedSinceLastSample = std::chrono::duration<double>(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<double>(endFrameTime - startFrameTime) / 1ms;
avgFps += frameTimeMs;
sampledFps++;
if (const auto elapsedSinceLastSample = std::chrono::duration<double>(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(),

View File

@@ -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<n64::Core> &, double &, RenderWidget &, SettingsWindow &) noexcept;
explicit EmuThread(const std::shared_ptr<n64::Core> &, double &, RenderWidget &, SettingsWindow &, KaizenGui&) noexcept;
void start() noexcept;
void TogglePause() const noexcept;

View File

@@ -0,0 +1,157 @@
#pragma once
#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES
#include <imgui.h>
#include <imgui_impl_sdl3.h>
#include <imgui_impl_vulkan.h>
#include <utils/log.hpp>
#include <memory>
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<Vulkan::WSI> 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<Vulkan::WSI>& 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<VkInstance>(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());
}
}

View File

@@ -23,6 +23,8 @@ struct StatusBar {
if(exec)
exec();
ImGui::End();
return true;
}
private:
std::function<void()> exec = nullptr;

View File

@@ -38,6 +38,8 @@ bool InputSettings::render() {
i++;
}
return true;
}
std::array<SDL_Keycode, 18> InputSettings::GetMappedKeys() {

View File

@@ -2,20 +2,12 @@
#include <nfd.hpp>
#include <backend/Core.hpp>
#include <ImGuiImpl/StatusBar.hpp>
#include <imgui.h>
#include <imgui_impl_sdl3.h>
#include <imgui_impl_vulkan.h>
#include <ImGuiImpl/GUI.hpp>
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<n64::Core>()), vulkanWidget(core, window.getHandle()), emuThread(core, fpsCounter, vulkanWidget, settingsWindow) {
KaizenGui::KaizenGui() noexcept : window("Kaizen", 1280, 720), core(std::make_shared<n64::Core>()), 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;

View File

@@ -1,6 +1,6 @@
#pragma once
#include <NativeWindow.hpp>
#include <RenderWidget.hpp>
#include <NativeWindow.hpp>
#include <Debugger.hpp>
#include <ImGuiImpl/Menu.hpp>
#include <ImGuiImpl/StatusBar.hpp>
@@ -18,6 +18,9 @@ public:
std::shared_ptr<n64::Core> 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<void()> emuExitFunc;
gui::PopupWindow about{"About Kaizen"};
gui::StatusBar statusBar{};
void InitImGui();
};

View File

@@ -2,12 +2,18 @@
#include <SDL3/SDL.h>
#include <string>
#include <memory>
#include <volk.h>
#include <utils/log.hpp>
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() {

View File

@@ -4,12 +4,9 @@
#include <SDL3/SDL.h>
#include <InputSettings.hpp>
RenderWidget::RenderWidget(const std::shared_ptr<n64::Core> &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<SDLWSIPlatform>(core, inputSettings, window);
RenderWidget::RenderWidget(const std::shared_ptr<n64::Core> &core, SDL_Window* window) {
wsiPlatform = std::make_shared<SDLWSIPlatform>(core, window);
windowInfo = std::make_shared<SDLParallelRdpWindowInfo>(window);
}
void SDLWSIPlatform::poll_input() {

View File

@@ -11,20 +11,20 @@ struct Core;
class SDLParallelRdpWindowInfo final : public ParallelRDP::WindowInfo {
public:
explicit SDLParallelRdpWindowInfo(const std::shared_ptr<SDL_Window> 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<float>(w), static_cast<float>(h)};
}
private:
std::shared_ptr<SDL_Window> window{};
SDL_Window* window{};
};
class SDLWSIPlatform final : public Vulkan::WSIPlatform {
public:
explicit SDLWSIPlatform(const std::shared_ptr<n64::Core> &core, InputSettings& inputSettings, SDL_Window* window) : window(window), inputSettings(inputSettings), core(core) {}
explicit SDLWSIPlatform(const std::shared_ptr<n64::Core> &core, SDL_Window* window) : window(window), core(core) {}
std::vector<const char *> get_instance_extensions() override {
auto vec = std::vector<const char *>();
@@ -69,16 +69,14 @@ public:
private:
std::shared_ptr<n64::Core> core;
SDL_Gamepad *gamepad{};
InputSettings& inputSettings;
bool gamepadConnected = false;
bool canPollEvents = true;
};
class RenderWidget final {
public:
explicit RenderWidget(const std::shared_ptr<n64::Core> &, InputSettings&, SDL_Window*);
explicit RenderWidget(const std::shared_ptr<n64::Core> &, SDL_Window*);
std::shared_ptr<ParallelRDP::WindowInfo> windowInfo;
std::shared_ptr<Vulkan::WSIPlatform> wsiPlatform;
InputSettings& inputSettings;
};