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
|
|
||||||
14
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
14
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
@@ -5,6 +5,7 @@
|
|||||||
#include <core/mmio/VI.hpp>
|
#include <core/mmio/VI.hpp>
|
||||||
#include <resources/vert.spv.h>
|
#include <resources/vert.spv.h>
|
||||||
#include <resources/frag.spv.h>
|
#include <resources/frag.spv.h>
|
||||||
|
#include <KaizenGui.hpp>
|
||||||
|
|
||||||
using namespace Vulkan;
|
using namespace Vulkan;
|
||||||
using namespace RDP;
|
using namespace RDP;
|
||||||
@@ -156,7 +157,7 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
|
|||||||
cmd->draw(3, 1);
|
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();
|
wsi->begin_frame();
|
||||||
|
|
||||||
if (!image) {
|
if (!image) {
|
||||||
@@ -182,13 +183,14 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
|
|||||||
|
|
||||||
cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly));
|
cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly));
|
||||||
DrawFullscreenTexturedQuad(image, cmd);
|
DrawFullscreenTexturedQuad(image, cmd);
|
||||||
|
kaizenGui.RenderUI();
|
||||||
cmd->end_render_pass();
|
cmd->end_render_pass();
|
||||||
wsi->get_device().submit(cmd);
|
wsi->get_device().submit(cmd);
|
||||||
wsi->end_frame();
|
wsi->end_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParallelRDP::UpdateScreen(const n64::VI &vi) const {
|
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::Control, vi.status.raw);
|
||||||
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
||||||
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
||||||
@@ -213,8 +215,12 @@ void ParallelRDP::UpdateScreen(const n64::VI &vi) const {
|
|||||||
opts.downscale_steps = true;
|
opts.downscale_steps = true;
|
||||||
opts.crop_overscan_pixels = true;
|
opts.crop_overscan_pixels = true;
|
||||||
Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
||||||
UpdateScreen(image);
|
UpdateScreen(image, kaizenGui);
|
||||||
command_processor->begin_frame_context();
|
command_processor->begin_frame_context();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr), kaizenGui);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParallelRDP::EnqueueCommand(int command_length, const u32 *buffer) const {
|
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;
|
struct VI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KaizenGui;
|
||||||
|
|
||||||
class ParallelRDP {
|
class ParallelRDP {
|
||||||
public:
|
public:
|
||||||
class WindowInfo {
|
class WindowInfo {
|
||||||
@@ -23,7 +25,7 @@ public:
|
|||||||
const std::shared_ptr<WindowInfo> &, const u8 *);
|
const std::shared_ptr<WindowInfo> &, const u8 *);
|
||||||
ParallelRDP() = default;
|
ParallelRDP() = default;
|
||||||
|
|
||||||
void UpdateScreen(const n64::VI &) const;
|
void UpdateScreen(const n64::VI &, KaizenGui&, bool = true) const;
|
||||||
void EnqueueCommand(int, const u32 *) const;
|
void EnqueueCommand(int, const u32 *) const;
|
||||||
void OnFullSync() const;
|
void OnFullSync() const;
|
||||||
bool IsFramerateUnlocked() const;
|
bool IsFramerateUnlocked() const;
|
||||||
@@ -36,5 +38,5 @@ public:
|
|||||||
private:
|
private:
|
||||||
void LoadWSIPlatform(const std::shared_ptr<Vulkan::WSIPlatform> &, const std::shared_ptr<WindowInfo> &);
|
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 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)
|
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
add_compile_definitions(VK_NO_PROTOTYPES)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
|
||||||
enable_language(OBJC)
|
enable_language(OBJC)
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <EmuThread.hpp>
|
#include <EmuThread.hpp>
|
||||||
|
#include <KaizenGui.hpp>
|
||||||
|
|
||||||
EmuThread::EmuThread(const std::shared_ptr<n64::Core> &core, double &fps, RenderWidget &renderWidget,
|
EmuThread::EmuThread(const std::shared_ptr<n64::Core> &core, double &fps, RenderWidget &renderWidget,
|
||||||
SettingsWindow &settings) noexcept :
|
SettingsWindow &settings, KaizenGui& kaizenGui) noexcept :
|
||||||
renderWidget(renderWidget), core(core), settings(settings), fps(fps) {}
|
renderWidget(renderWidget), core(core), settings(settings), fps(fps) {
|
||||||
|
|
||||||
void EmuThread::start() noexcept {
|
thread = std::thread([&]() {
|
||||||
thread = std::thread([this]() {
|
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
core->parallel.Init(renderWidget.wsiPlatform, renderWidget.windowInfo,
|
|
||||||
core->cpu->GetMem().GetRDRAMPtr());
|
|
||||||
|
|
||||||
auto lastSample = std::chrono::high_resolution_clock::now();
|
auto lastSample = std::chrono::high_resolution_clock::now();
|
||||||
auto avgFps = 16.667;
|
auto avgFps = 16.667;
|
||||||
@@ -19,13 +17,40 @@ void EmuThread::start() noexcept {
|
|||||||
fps = 1000.0 / avgFps;
|
fps = 1000.0 / avgFps;
|
||||||
|
|
||||||
while (!interruptionRequested) {
|
while (!interruptionRequested) {
|
||||||
|
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();
|
const auto startFrameTime = std::chrono::high_resolution_clock::now();
|
||||||
if (!core->pause) {
|
if (!core->pause) {
|
||||||
core->Run(settings.getVolumeL(), settings.getVolumeR());
|
core->Run(settings.getVolumeL(), settings.getVolumeR());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (core->render) {
|
if (core->render) {
|
||||||
core->parallel.UpdateScreen(core->cpu->GetMem().mmio.vi);
|
core->parallel.UpdateScreen(core->cpu->GetMem().mmio.vi, kaizenGui);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto endFrameTime = std::chrono::high_resolution_clock::now();
|
const auto endFrameTime = std::chrono::high_resolution_clock::now();
|
||||||
@@ -47,12 +72,15 @@ void EmuThread::start() noexcept {
|
|||||||
fps = 1000.0 / avgFps;
|
fps = 1000.0 / avgFps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
SetRender(false);
|
SetRender(false);
|
||||||
Stop();
|
Stop();
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmuThread::start() noexcept { started = true; }
|
||||||
|
|
||||||
void EmuThread::TogglePause() const noexcept {
|
void EmuThread::TogglePause() const noexcept {
|
||||||
core->TogglePause();
|
core->TogglePause();
|
||||||
Util::RPC::GetInstance().Update(core->pause ? Util::RPC::Paused : Util::RPC::GetInstance().GetState(),
|
Util::RPC::GetInstance().Update(core->pause ? Util::RPC::Paused : Util::RPC::GetInstance().GetState(),
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ namespace n64 {
|
|||||||
struct Core;
|
struct Core;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KaizenGui;
|
||||||
|
|
||||||
class EmuThread final {
|
class EmuThread final {
|
||||||
RenderWidget &renderWidget;
|
RenderWidget &renderWidget;
|
||||||
|
bool started = false;
|
||||||
public:
|
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 start() noexcept;
|
||||||
void TogglePause() const 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)
|
if(exec)
|
||||||
exec();
|
exec();
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
std::function<void()> exec = nullptr;
|
std::function<void()> exec = nullptr;
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ bool InputSettings::render() {
|
|||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<SDL_Keycode, 18> InputSettings::GetMappedKeys() {
|
std::array<SDL_Keycode, 18> InputSettings::GetMappedKeys() {
|
||||||
|
|||||||
@@ -2,20 +2,12 @@
|
|||||||
#include <nfd.hpp>
|
#include <nfd.hpp>
|
||||||
#include <backend/Core.hpp>
|
#include <backend/Core.hpp>
|
||||||
#include <ImGuiImpl/StatusBar.hpp>
|
#include <ImGuiImpl/StatusBar.hpp>
|
||||||
#include <imgui.h>
|
#include <ImGuiImpl/GUI.hpp>
|
||||||
#include <imgui_impl_sdl3.h>
|
|
||||||
#include <imgui_impl_vulkan.h>
|
|
||||||
|
|
||||||
static void check_vk_result(VkResult err)
|
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());
|
||||||
if (err == 0)
|
gui::Initialize(core->parallel.wsi, window.getHandle());
|
||||||
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) {
|
|
||||||
emuExitFunc = [&]() {
|
emuExitFunc = [&]() {
|
||||||
quit = true;
|
quit = true;
|
||||||
if (emuThread.isRunning) {
|
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("Kaizen is a Nintendo 64 emulator that strives");
|
||||||
ImGui::Text("to offer a friendly user experience and compatibility.");
|
ImGui::Text("to offer a friendly user experience and compatibility.");
|
||||||
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
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")) {
|
if(ImGui::Button("OK")) {
|
||||||
about.setOpened(false);
|
about.setOpened(false);
|
||||||
}
|
}
|
||||||
@@ -55,24 +47,34 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 1280, 720), core(std::make_sh
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
menuBar.addMenu({"Emulation",
|
menuBar.addMenu({
|
||||||
{
|
"Emulation", {
|
||||||
actionPause,
|
actionPause,
|
||||||
actionStop,
|
actionStop,
|
||||||
actionReset,
|
actionReset,
|
||||||
{"Settings", [&]() {
|
{"Settings", [&]() {
|
||||||
settingsWindow.render();
|
settingsWindow.render();
|
||||||
}},
|
}},
|
||||||
}
|
}});
|
||||||
});
|
|
||||||
|
|
||||||
menuBar.addMenu({"Help",
|
menuBar.addMenu({
|
||||||
{
|
"Help", {
|
||||||
{"About", [&]() {
|
{"About", [&]() {
|
||||||
about.setOpened(true);
|
about.setOpened(true);
|
||||||
}},
|
}},
|
||||||
|
}});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
void KaizenGui::RenderUI() {
|
||||||
|
gui::StartFrame();
|
||||||
|
|
||||||
|
menuBar.render();
|
||||||
|
|
||||||
|
about.render();
|
||||||
|
|
||||||
|
statusBar.render();
|
||||||
|
|
||||||
|
gui::EndFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
||||||
@@ -88,6 +90,7 @@ void KaizenGui::LoadROM(const std::string &path) noexcept {
|
|||||||
void KaizenGui::handleEvents() {
|
void KaizenGui::handleEvents() {
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
while(SDL_PollEvent(&e)) {
|
while(SDL_PollEvent(&e)) {
|
||||||
|
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||||
switch(e.type) {
|
switch(e.type) {
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
emuExitFunc();
|
emuExitFunc();
|
||||||
@@ -99,12 +102,6 @@ void KaizenGui::handleEvents() {
|
|||||||
int KaizenGui::run() {
|
int KaizenGui::run() {
|
||||||
while(!quit) {
|
while(!quit) {
|
||||||
handleEvents();
|
handleEvents();
|
||||||
|
|
||||||
menuBar.render();
|
|
||||||
|
|
||||||
about.render();
|
|
||||||
|
|
||||||
statusBar.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <NativeWindow.hpp>
|
|
||||||
#include <RenderWidget.hpp>
|
#include <RenderWidget.hpp>
|
||||||
|
#include <NativeWindow.hpp>
|
||||||
#include <Debugger.hpp>
|
#include <Debugger.hpp>
|
||||||
#include <ImGuiImpl/Menu.hpp>
|
#include <ImGuiImpl/Menu.hpp>
|
||||||
#include <ImGuiImpl/StatusBar.hpp>
|
#include <ImGuiImpl/StatusBar.hpp>
|
||||||
@@ -18,6 +18,9 @@ public:
|
|||||||
std::shared_ptr<n64::Core> core;
|
std::shared_ptr<n64::Core> core;
|
||||||
RenderWidget vulkanWidget;
|
RenderWidget vulkanWidget;
|
||||||
EmuThread emuThread;
|
EmuThread emuThread;
|
||||||
|
gui::PopupWindow about{"About Kaizen"};
|
||||||
|
gui::StatusBar statusBar{};
|
||||||
|
void RenderUI();
|
||||||
|
|
||||||
int run();
|
int run();
|
||||||
void LoadTAS(const std::string &path) const noexcept;
|
void LoadTAS(const std::string &path) const noexcept;
|
||||||
@@ -26,7 +29,4 @@ private:
|
|||||||
bool quit = false;
|
bool quit = false;
|
||||||
void handleEvents();
|
void handleEvents();
|
||||||
std::function<void()> emuExitFunc;
|
std::function<void()> emuExitFunc;
|
||||||
gui::PopupWindow about{"About Kaizen"};
|
|
||||||
gui::StatusBar statusBar{};
|
|
||||||
void InitImGui();
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,12 +2,18 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <volk.h>
|
||||||
|
#include <utils/log.hpp>
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
struct NativeWindow {
|
struct NativeWindow {
|
||||||
NativeWindow(const std::string& title, int w, int h, int posX = SDL_WINDOWPOS_CENTERED, int posY = SDL_WINDOWPOS_CENTERED) {
|
NativeWindow(const std::string& title, int w, int h, int posX = SDL_WINDOWPOS_CENTERED, int posY = SDL_WINDOWPOS_CENTERED) {
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
window = SDL_CreateWindow(title.c_str(), w, h, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY);
|
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() {
|
~NativeWindow() {
|
||||||
|
|||||||
@@ -4,12 +4,9 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <InputSettings.hpp>
|
#include <InputSettings.hpp>
|
||||||
|
|
||||||
RenderWidget::RenderWidget(const std::shared_ptr<n64::Core> &core, InputSettings& inputSettings, SDL_Window* window) : inputSettings(inputSettings) {
|
RenderWidget::RenderWidget(const std::shared_ptr<n64::Core> &core, SDL_Window* window) {
|
||||||
if (!Vulkan::Context::init_loader(nullptr)) {
|
wsiPlatform = std::make_shared<SDLWSIPlatform>(core, window);
|
||||||
Util::panic("Could not initialize Vulkan ICD");
|
windowInfo = std::make_shared<SDLParallelRdpWindowInfo>(window);
|
||||||
}
|
|
||||||
|
|
||||||
wsiPlatform = std::make_shared<SDLWSIPlatform>(core, inputSettings, window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLWSIPlatform::poll_input() {
|
void SDLWSIPlatform::poll_input() {
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ struct Core;
|
|||||||
|
|
||||||
class SDLParallelRdpWindowInfo final : public ParallelRDP::WindowInfo {
|
class SDLParallelRdpWindowInfo final : public ParallelRDP::WindowInfo {
|
||||||
public:
|
public:
|
||||||
explicit SDLParallelRdpWindowInfo(const std::shared_ptr<SDL_Window> window) : window(window) {}
|
explicit SDLParallelRdpWindowInfo(SDL_Window* window) : window(window) {}
|
||||||
CoordinatePair get_window_size() override {
|
CoordinatePair get_window_size() override {
|
||||||
int w,h;
|
int w,h;
|
||||||
SDL_GetWindowSizeInPixels(window.get(), &w, &h);
|
SDL_GetWindowSizeInPixels(window, &w, &h);
|
||||||
return CoordinatePair{static_cast<float>(w), static_cast<float>(h)};
|
return CoordinatePair{static_cast<float>(w), static_cast<float>(h)};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<SDL_Window> window{};
|
SDL_Window* window{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class SDLWSIPlatform final : public Vulkan::WSIPlatform {
|
class SDLWSIPlatform final : public Vulkan::WSIPlatform {
|
||||||
public:
|
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 {
|
std::vector<const char *> get_instance_extensions() override {
|
||||||
auto vec = std::vector<const char *>();
|
auto vec = std::vector<const char *>();
|
||||||
@@ -69,16 +69,14 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::shared_ptr<n64::Core> core;
|
std::shared_ptr<n64::Core> core;
|
||||||
SDL_Gamepad *gamepad{};
|
SDL_Gamepad *gamepad{};
|
||||||
InputSettings& inputSettings;
|
|
||||||
bool gamepadConnected = false;
|
bool gamepadConnected = false;
|
||||||
bool canPollEvents = true;
|
bool canPollEvents = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RenderWidget final {
|
class RenderWidget final {
|
||||||
public:
|
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<ParallelRDP::WindowInfo> windowInfo;
|
||||||
std::shared_ptr<Vulkan::WSIPlatform> wsiPlatform;
|
std::shared_ptr<Vulkan::WSIPlatform> wsiPlatform;
|
||||||
InputSettings& inputSettings;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user