Some progress
This commit is contained in:
60
.github/workflows/build.yml
vendored
60
.github/workflows/build.yml
vendored
@@ -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
|
||||
64
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
64
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
@@ -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 {
|
||||
|
||||
6
external/parallel-rdp/ParallelRDPWrapper.hpp
vendored
6
external/parallel-rdp/ParallelRDPWrapper.hpp
vendored
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(!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;
|
||||
|
||||
if (core->render) {
|
||||
core->parallel.UpdateScreen(core->cpu->GetMem().mmio.vi);
|
||||
}
|
||||
sampledFps++;
|
||||
|
||||
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 (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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
157
src/frontend/ImGuiImpl/GUI.hpp
Normal file
157
src/frontend/ImGuiImpl/GUI.hpp
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ struct StatusBar {
|
||||
if(exec)
|
||||
exec();
|
||||
ImGui::End();
|
||||
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
std::function<void()> exec = nullptr;
|
||||
|
||||
@@ -38,6 +38,8 @@ bool InputSettings::render() {
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::array<SDL_Keycode, 18> InputSettings::GetMappedKeys() {
|
||||
|
||||
@@ -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, *this) {
|
||||
core->parallel.Init(vulkanWidget.wsiPlatform, vulkanWidget.windowInfo, core->cpu->GetMem().GetRDRAMPtr());
|
||||
gui::Initialize(core->parallel.wsi, window.getHandle());
|
||||
|
||||
KaizenGui::KaizenGui() noexcept : window("Kaizen", 1280, 720), core(std::make_shared<n64::Core>()), vulkanWidget(core, window.getHandle()), emuThread(core, fpsCounter, vulkanWidget, settingsWindow) {
|
||||
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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user