Some progress

This commit is contained in:
SimoneN64
2024-09-20 23:03:53 +02:00
parent c5cff64ea1
commit 1a69c58458
10 changed files with 658 additions and 192 deletions

View File

@@ -6,6 +6,4 @@ file(GLOB HEADERS *.h)
list(APPEND SOURCES backends/imgui_impl_sdl3.cpp backends/imgui_impl_vulkan.cpp) list(APPEND SOURCES backends/imgui_impl_sdl3.cpp backends/imgui_impl_vulkan.cpp)
add_library(imgui ${SOURCES} ${HEADERS}) add_library(imgui ${SOURCES} ${HEADERS})
target_include_directories(imgui PRIVATE . backends ../SDL/include) target_include_directories(imgui PRIVATE . backends ../SDL/include)
target_compile_definitions(imgui PRIVATE IMGUI_IMPL_VULKAN_USE_VOLK)
target_compile_definitions(imgui PRIVATE IMGUI_IMPL_VULKAN_NO_PROTOTYPES)

View File

@@ -6,6 +6,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
add_compile_definitions(IMGUI_IMPL_VULKAN_USE_VOLK)
add_compile_definitions(IMGUI_IMPL_VULKAN_NO_PROTOTYPES)
if (WIN32) if (WIN32)
add_compile_definitions(NOMINMAX) add_compile_definitions(NOMINMAX)
endif () endif ()
@@ -68,7 +71,6 @@ add_executable(kaizen-qt
RenderWidget.hpp RenderWidget.hpp
EmuThread.hpp EmuThread.hpp
EmuThread.cpp EmuThread.cpp
mainwindow.ui
MainWindow.hpp MainWindow.hpp
MainWindow.cpp MainWindow.cpp
SettingsWindow.hpp SettingsWindow.hpp
@@ -79,7 +81,9 @@ add_executable(kaizen-qt
AudioSettings.hpp AudioSettings.hpp
AudioSettings.cpp AudioSettings.cpp
InputSettings.hpp InputSettings.hpp
InputSettings.cpp) InputSettings.cpp
ImGuiWidget.hpp
ImGuiWidget.cpp)
include(CheckCCompilerFlag) include(CheckCCompilerFlag)

View File

@@ -0,0 +1,444 @@
#include <ImGuiWidget.hpp>
#include <log.hpp>
#include <QWindow>
#include <qevent.h>
#include <SDL3/SDL_vulkan.h>
static void check_vk_result(VkResult err) {
if (err == 0)
return;
fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
if (err < 0)
abort();
}
#ifdef APP_USE_VULKAN_DEBUG_REPORT
static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType,
uint64_t object, size_t location, int32_t messageCode,
const char *pLayerPrefix, const char *pMessage, void *pUserData) {
(void)flags;
(void)object;
(void)location;
(void)messageCode;
(void)pUserData;
(void)pLayerPrefix; // Unused arguments
fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage);
return VK_FALSE;
}
#endif // APP_USE_VULKAN_DEBUG_REPORT
static bool IsExtensionAvailable(const std::vector<VkExtensionProperties> &properties, const char *extension) {
for (const VkExtensionProperties &p : properties)
if (strcmp(p.extensionName, extension) == 0)
return true;
return false;
}
void ImGuiWidget::SelectGpu() {
uint32_t gpu_count;
VkResult err = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr);
check_vk_result(err);
IM_ASSERT(gpu_count > 0);
std::vector<VkPhysicalDevice> gpus;
gpus.resize(gpu_count);
err = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.data());
check_vk_result(err);
// If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers
// most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple
// dedicated GPUs) is out of scope of this sample.
for (VkPhysicalDevice &device : gpus) {
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(device, &properties);
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
gpu = device;
return;
}
}
// Use first GPU (Integrated) if a Discrete one is not available.
if (gpu_count > 0) {
gpu = gpus[0];
return;
}
Util::panic("[ImGui Widget] Could not find a suitable Vulkan GPU!");
}
void ImGuiWidget::InitVulkan(char const *const *instanceExtensions, uint32_t instanceExtensionsCount) {
VkResult err;
#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
volkInitialize();
#endif
// Create Vulkan Instance
{
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
// Enumerate available extensions
uint32_t properties_count;
std::vector<VkExtensionProperties> properties;
vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, nullptr);
properties.resize(properties_count);
err = vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, properties.data());
check_vk_result(err);
std::vector<const char *> extensions;
extensions.resize(instanceExtensionsCount);
memcpy(extensions.data(), instanceExtensions, instanceExtensionsCount * sizeof(const char *));
// Enable required extensions
if (IsExtensionAvailable(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
}
#endif
// Enabling validation layers
#ifdef APP_USE_VULKAN_DEBUG_REPORT
const char *layers[] = {"VK_LAYER_KHRONOS_validation"};
create_info.enabledLayerCount = 1;
create_info.ppEnabledLayerNames = layers;
instance_extensions.push_back("VK_EXT_debug_report");
#endif
// Create Vulkan Instance
create_info.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
create_info.ppEnabledExtensionNames = extensions.data();
err = vkCreateInstance(&create_info, nullptr, &instance);
check_vk_result(err);
#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
volkLoadInstance(instance);
#endif
// Setup the debug report callback
#ifdef APP_USE_VULKAN_DEBUG_REPORT
auto f_vkCreateDebugReportCallbackEXT =
(PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT");
IM_ASSERT(f_vkCreateDebugReportCallbackEXT != nullptr);
VkDebugReportCallbackCreateInfoEXT debug_report_ci = {};
debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
debug_report_ci.flags =
VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
debug_report_ci.pfnCallback = debug_report;
debug_report_ci.pUserData = nullptr;
err = f_vkCreateDebugReportCallbackEXT(instance, &debug_report_ci, nullptr, &g_DebugReport);
check_vk_result(err);
#endif
}
// Select Physical Device (GPU)
SelectGpu();
// Select graphics queue family
{
uint32_t count;
vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, nullptr);
VkQueueFamilyProperties *queues = (VkQueueFamilyProperties *)malloc(sizeof(VkQueueFamilyProperties) * count);
vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, queues);
for (uint32_t i = 0; i < count; i++)
if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
queueFamily = i;
break;
}
free(queues);
IM_ASSERT(queueFamily != (uint32_t)-1);
}
// Create Logical Device (with 1 queue)
{
std::vector<const char *> device_extensions;
device_extensions.push_back("VK_KHR_swapchain");
// Enumerate physical device extension
uint32_t properties_count;
std::vector<VkExtensionProperties> properties;
vkEnumerateDeviceExtensionProperties(gpu, nullptr, &properties_count, nullptr);
properties.resize(properties_count);
vkEnumerateDeviceExtensionProperties(gpu, nullptr, &properties_count, properties.data());
#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME
if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME))
device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
#endif
const float queue_priority[] = {1.0f};
VkDeviceQueueCreateInfo queue_info[1] = {};
queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info[0].queueFamilyIndex = queueFamily;
queue_info[0].queueCount = 1;
queue_info[0].pQueuePriorities = queue_priority;
VkDeviceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]);
create_info.pQueueCreateInfos = queue_info;
create_info.enabledExtensionCount = (uint32_t)device_extensions.size();
create_info.ppEnabledExtensionNames = device_extensions.data();
err = vkCreateDevice(gpu, &create_info, nullptr, &device);
check_vk_result(err);
vkGetDeviceQueue(device, queueFamily, 0, &queue);
}
// Create Descriptor Pool
// The example only requires a single combined image sampler descriptor for the font image and only uses one
// descriptor set (for that) If you wish to load e.g. additional textures you may need to alter pools sizes.
{
VkDescriptorPoolSize pool_sizes[] = {
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1},
};
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 = 1;
pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
pool_info.pPoolSizes = pool_sizes;
err = vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptorPool);
check_vk_result(err);
}
}
void ImGuiWidget::InitWindow() {
mainWindowData.Surface = surface;
// Check for WSI support
VkBool32 res;
vkGetPhysicalDeviceSurfaceSupportKHR(gpu, queueFamily, mainWindowData.Surface, &res);
if (res != VK_TRUE) {
fprintf(stderr, "Error no WSI support on physical device 0\n");
exit(-1);
}
// Select Surface Format
const VkFormat requestSurfaceImageFormat[] = {VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM};
const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
mainWindowData.SurfaceFormat =
ImGui_ImplVulkanH_SelectSurfaceFormat(gpu, mainWindowData.Surface, requestSurfaceImageFormat,
(size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace);
// Select Present Mode
#ifdef APP_UNLIMITED_FRAME_RATE
VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR,
VK_PRESENT_MODE_FIFO_KHR};
#else
VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_FIFO_KHR};
#endif
mainWindowData.PresentMode =
ImGui_ImplVulkanH_SelectPresentMode(gpu, mainWindowData.Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
// printf("[vulkan] Selected PresentMode = %d\n", mainWindowData.PresentMode);
// Create SwapChain, RenderPass, Framebuffer, etc.
IM_ASSERT(minImageCount >= 2);
ImGui_ImplVulkanH_CreateOrResizeWindow(instance, gpu, device, &mainWindowData, queueFamily, nullptr, width(),
height(), minImageCount);
}
ImGuiWidget::ImGuiWidget(QWidget *parent) : QWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_NoSystemBackground);
setFocusPolicy(Qt::StrongFocus);
// HOWEVER, this important attribute stops the "paintEvent" slot from being called,
// thus we'll need to write our own method that paints to the screen every frame.
setAttribute(Qt::WA_PaintOnScreen);
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
SDL_InitSubSystem(SDL_INIT_VIDEO);
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
auto winPtr = reinterpret_cast<void *>(winId());
auto props = SDL_CreateProperties();
#ifdef SDL_PLATFORM_LINUX
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER, winPtr);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, reinterpret_cast<s64>(winPtr));
#elif SDL_PLATFORM_WINDOWS
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER, winPtr);
#else
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER, winPtr);
#endif
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN, true);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, true);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true);
window = SDL_CreateWindowWithProperties(props);
uint32_t extensionCount = 0;
auto extensions = SDL_Vulkan_GetInstanceExtensions(&extensionCount);
InitVulkan(extensions, extensionCount);
if (!SDL_Vulkan_CreateSurface(window, instance, nullptr, &surface)) {
Util::panic("[ImGui Widget]: Failed to create Vulkan surface.\n");
}
InitWindow();
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons;
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge;
// Setup Dear ImGui style
ImGui::StyleColorsDark();
// ImGui::StyleColorsLight();
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ImGuiStyle &style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
// Setup Platform/Renderer backends
ImGui_ImplSDL3_InitForVulkan(window);
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = instance;
init_info.PhysicalDevice = gpu;
init_info.Device = device;
init_info.QueueFamily = queueFamily;
init_info.Queue = queue;
init_info.PipelineCache = pipelineCache;
init_info.DescriptorPool = descriptorPool;
init_info.RenderPass = mainWindowData.RenderPass;
init_info.Subpass = 0;
init_info.MinImageCount = minImageCount;
init_info.ImageCount = mainWindowData.ImageCount;
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.Allocator = nullptr;
init_info.CheckVkResultFn = check_vk_result;
ImGui_ImplVulkan_Init(&init_info);
}
void ImGuiWidget::resizeEvent(QResizeEvent *event) {
if (event->size().width() > 0 && event->size().height() > 0 &&
(swapChainRebuild || mainWindowData.Width != event->size().width() ||
mainWindowData.Height != event->size().height())) {
ImGui_ImplVulkan_SetMinImageCount(minImageCount);
ImGui_ImplVulkanH_CreateOrResizeWindow(instance, gpu, device, &mainWindowData, queueFamily, nullptr,
event->size().width(), event->size().height(), minImageCount);
mainWindowData.FrameIndex = 0;
swapChainRebuild = false;
}
}
void ImGuiWidget::showEvent(QShowEvent *) {
connect(&timer, &QTimer::timeout, this, &ImGuiWidget::Repaint);
timer.setInterval(16);
timer.start();
}
void ImGuiWidget::Repaint() {
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
ImGui::ShowDemoWindow();
ImGui::Render();
FrameRender(ImGui::GetDrawData());
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
FramePresent();
}
void ImGuiWidget::FrameRender(ImDrawData *drawData) {
VkResult err;
VkSemaphore image_acquired_semaphore =
mainWindowData.FrameSemaphores[mainWindowData.SemaphoreIndex].ImageAcquiredSemaphore;
VkSemaphore render_complete_semaphore =
mainWindowData.FrameSemaphores[mainWindowData.SemaphoreIndex].RenderCompleteSemaphore;
err = vkAcquireNextImageKHR(device, mainWindowData.Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE,
&mainWindowData.FrameIndex);
if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {
swapChainRebuild = true;
return;
}
check_vk_result(err);
ImGui_ImplVulkanH_Frame *fd = &mainWindowData.Frames[mainWindowData.FrameIndex];
{
err = vkWaitForFences(device, 1, &fd->Fence, VK_TRUE,
UINT64_MAX); // wait indefinitely instead of periodically checking
check_vk_result(err);
err = vkResetFences(device, 1, &fd->Fence);
check_vk_result(err);
}
{
err = vkResetCommandPool(device, fd->CommandPool, 0);
check_vk_result(err);
VkCommandBufferBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
check_vk_result(err);
}
{
VkRenderPassBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.renderPass = mainWindowData.RenderPass;
info.framebuffer = fd->Framebuffer;
info.renderArea.extent.width = mainWindowData.Width;
info.renderArea.extent.height = mainWindowData.Height;
info.clearValueCount = 1;
info.pClearValues = &mainWindowData.ClearValue;
vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
}
// Record dear imgui primitives into command buffer
ImGui_ImplVulkan_RenderDrawData(drawData, fd->CommandBuffer);
// Submit command buffer
vkCmdEndRenderPass(fd->CommandBuffer);
{
VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
info.waitSemaphoreCount = 1;
info.pWaitSemaphores = &image_acquired_semaphore;
info.pWaitDstStageMask = &wait_stage;
info.commandBufferCount = 1;
info.pCommandBuffers = &fd->CommandBuffer;
info.signalSemaphoreCount = 1;
info.pSignalSemaphores = &render_complete_semaphore;
err = vkEndCommandBuffer(fd->CommandBuffer);
check_vk_result(err);
err = vkQueueSubmit(queue, 1, &info, fd->Fence);
check_vk_result(err);
}
}
void ImGuiWidget::FramePresent() {
if (swapChainRebuild)
return;
VkSemaphore render_complete_semaphore =
mainWindowData.FrameSemaphores[mainWindowData.SemaphoreIndex].RenderCompleteSemaphore;
VkPresentInfoKHR info = {};
info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
info.waitSemaphoreCount = 1;
info.pWaitSemaphores = &render_complete_semaphore;
info.swapchainCount = 1;
info.pSwapchains = &mainWindowData.Swapchain;
info.pImageIndices = &mainWindowData.FrameIndex;
VkResult err = vkQueuePresentKHR(queue, &info);
if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {
swapChainRebuild = true;
return;
}
check_vk_result(err);
mainWindowData.SemaphoreIndex =
(mainWindowData.SemaphoreIndex + 1) % mainWindowData.SemaphoreCount; // Now we can use the next set of semaphores
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include <imgui.h>
#include <imgui_impl_sdl3.h>
#include <imgui_impl_vulkan.h>
#include <SDL3/SDL.h>
#include <QTimer>
#include <QWidget>
class ImGuiWidget : public QWidget {
QTimer timer;
SDL_Window *window{};
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice gpu = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
uint32_t queueFamily = static_cast<uint32_t>(-1);
VkQueue queue = VK_NULL_HANDLE;
VkDebugReportCallbackEXT debugReport = VK_NULL_HANDLE;
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
VkSurfaceKHR surface;
ImGui_ImplVulkanH_Window mainWindowData;
uint32_t minImageCount = 2;
bool swapChainRebuild = false;
void SelectGpu();
void InitVulkan(char const *const *, uint32_t);
void InitWindow();
void FrameRender(ImDrawData *);
void FramePresent();
public slots:
void Repaint();
public:
[[nodiscard]] QPaintEngine *paintEngine() const override { return nullptr; }
ImGuiWidget(QWidget* parent);
void resizeEvent(QResizeEvent *event) override;
void showEvent(QShowEvent *) override;
};

View File

@@ -8,9 +8,9 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
KaizenQt::KaizenQt() noexcept : QWidget(nullptr) { KaizenQt::KaizenQt() noexcept : QWidget(nullptr) {
mainWindow = std::make_unique<MainWindowController>(); mainWindow = std::make_unique<MainWindow>();
settingsWindow = std::make_unique<SettingsWindow>(); settingsWindow = std::make_unique<SettingsWindow>();
emuThread = std::make_unique<EmuThread>(*mainWindow->view.vulkanWidget, *settingsWindow); emuThread = std::make_unique<EmuThread>(*mainWindow->vulkanWidget, *settingsWindow);
ConnectMainWindowSignalsToSlots(); ConnectMainWindowSignalsToSlots();
Util::RPC::GetInstance().Update(Util::RPC::Idling); Util::RPC::GetInstance().Update(Util::RPC::Idling);
@@ -25,13 +25,13 @@ KaizenQt::KaizenQt() noexcept : QWidget(nullptr) {
} }
void KaizenQt::ConnectMainWindowSignalsToSlots() noexcept { void KaizenQt::ConnectMainWindowSignalsToSlots() noexcept {
connect(mainWindow.get(), &MainWindowController::OpenSettings, this, [this]() { settingsWindow->show(); }); connect(mainWindow.get(), &MainWindow::OpenSettings, this, [this]() { settingsWindow->show(); });
connect(mainWindow.get(), &MainWindowController::OpenROM, this, &KaizenQt::LoadROM); connect(mainWindow.get(), &MainWindow::OpenROM, this, &KaizenQt::LoadROM);
connect(mainWindow.get(), &MainWindowController::Exit, this, &KaizenQt::Quit); connect(mainWindow.get(), &MainWindow::Exit, this, &KaizenQt::Quit);
connect(mainWindow.get(), &MainWindowController::Reset, emuThread.get(), &EmuThread::Reset); connect(mainWindow.get(), &MainWindow::Reset, emuThread.get(), &EmuThread::Reset);
connect(mainWindow.get(), &MainWindowController::Stop, emuThread.get(), &EmuThread::Stop); connect(mainWindow.get(), &MainWindow::Stop, emuThread.get(), &EmuThread::Stop);
connect(mainWindow.get(), &MainWindowController::Stop, this, [this]() { mainWindow->setWindowTitle("Kaizen"); }); connect(mainWindow.get(), &MainWindow::Stop, this, [this]() { mainWindow->setWindowTitle("Kaizen"); });
connect(mainWindow.get(), &MainWindowController::Pause, emuThread.get(), &EmuThread::TogglePause); connect(mainWindow.get(), &MainWindow::Pause, emuThread.get(), &EmuThread::TogglePause);
} }
void KaizenQt::dragEnterEvent(QDragEnterEvent *event) { void KaizenQt::dragEnterEvent(QDragEnterEvent *event) {
@@ -46,9 +46,9 @@ void KaizenQt::dropEvent(QDropEvent *event) {
} }
void KaizenQt::LoadROM(const QString &fileName) noexcept { void KaizenQt::LoadROM(const QString &fileName) noexcept {
mainWindow->view.actionPause->setEnabled(true); mainWindow->actionPause->setEnabled(true);
mainWindow->view.actionReset->setEnabled(true); mainWindow->actionReset->setEnabled(true);
mainWindow->view.actionStop->setEnabled(true); mainWindow->actionStop->setEnabled(true);
emuThread->start(); emuThread->start();
emuThread->core.LoadROM(fileName.toStdString()); emuThread->core.LoadROM(fileName.toStdString());
auto gameNameDB = emuThread->core.cpu->GetMem().rom.gameNameDB; auto gameNameDB = emuThread->core.cpu->GetMem().rom.gameNameDB;

View File

@@ -35,7 +35,7 @@ public:
private: private:
void Quit() noexcept; void Quit() noexcept;
void ConnectMainWindowSignalsToSlots() noexcept; void ConnectMainWindowSignalsToSlots() noexcept;
std::unique_ptr<MainWindowController> mainWindow; std::unique_ptr<MainWindow> mainWindow;
std::unique_ptr<SettingsWindow> settingsWindow; std::unique_ptr<SettingsWindow> settingsWindow;
std::unique_ptr<EmuThread> emuThread; std::unique_ptr<EmuThread> emuThread;
}; };

View File

@@ -3,49 +3,157 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <QMessageBox> #include <QMessageBox>
#include <QSlider> #include <QSlider>
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>
MainWindowController::MainWindowController() noexcept { MainWindow::MainWindow() noexcept {
view.setupUi(this); if (objectName().isEmpty())
view.actionPause->setDisabled(true); setObjectName("MainWindow");
view.actionReset->setDisabled(true); resize(800, 646);
view.actionStop->setDisabled(true); actionAbout = new QAction(this);
view.vulkanWidget->hide(); actionAbout->setObjectName("actionAbout");
actionOpen = new QAction(this);
actionOpen->setObjectName("actionOpen");
actionExit = new QAction(this);
actionExit->setObjectName("actionExit");
actionPause = new QAction(this);
actionPause->setObjectName("actionPause");
actionReset = new QAction(this);
actionReset->setObjectName("actionReset");
actionStop = new QAction(this);
actionStop->setObjectName("actionStop");
actionSettings = new QAction(this);
actionSettings->setObjectName("actionSettings");
centralwidget = new QWidget(this);
centralwidget->setObjectName("centralwidget");
verticalLayout = new QVBoxLayout(centralwidget);
verticalLayout->setSpacing(0);
verticalLayout->setObjectName("verticalLayout");
verticalLayout->setContentsMargins(0, 0, 0, 0);
horizontalLayout = new QHBoxLayout(centralwidget);
horizontalLayout->setSpacing(0);
verticalLayout->setObjectName("horizontalLayout");
verticalLayout->setContentsMargins(0, 0, 0, 0);
vulkanWidget = new RenderWidget(centralwidget);
vulkanWidget->setObjectName("vulkanWidget");
debugger = new ImGuiWidget(centralwidget);
debugger->setObjectName("debugger");
horizontalLayout->addWidget(vulkanWidget);
horizontalLayout->addWidget(debugger);
verticalLayout->addLayout(horizontalLayout);
setCentralWidget(centralwidget);
menubar = new QMenuBar(this);
menubar->setObjectName("menubar");
menubar->setGeometry(QRect(0, 0, 800, 22));
menuFile = new QMenu(menubar);
menuFile->setObjectName("menuFile");
menuEmulation = new QMenu(menubar);
menuEmulation->setObjectName("menuEmulation");
menuAbout = new QMenu(menubar);
menuAbout->setObjectName("menuAbout");
setMenuBar(menubar);
statusbar = new QStatusBar(this);
statusbar->setObjectName("statusbar");
setStatusBar(statusbar);
menubar->addAction(menuFile->menuAction());
menubar->addAction(menuEmulation->menuAction());
menubar->addAction(menuAbout->menuAction());
menuFile->addAction(actionOpen);
menuFile->addAction(actionExit);
menuEmulation->addAction(actionSettings);
menuEmulation->addAction(actionPause);
menuEmulation->addAction(actionReset);
menuEmulation->addAction(actionStop);
menuAbout->addAction(actionAbout);
Retranslate();
QMetaObject::connectSlotsByName(this);
actionPause->setDisabled(true);
actionReset->setDisabled(true);
actionStop->setDisabled(true);
vulkanWidget->hide();
ConnectSignalsToSlots(); ConnectSignalsToSlots();
} }
void MainWindowController::ConnectSignalsToSlots() noexcept { void MainWindow::Retranslate() {
connect(view.actionOpen, &QAction::triggered, this, [this]() { setWindowTitle(QCoreApplication::translate("MainWindow", "Kaizen", nullptr));
actionAbout->setText(QCoreApplication::translate("MainWindow", "About Kaizen", nullptr));
#if QT_CONFIG(statustip)
actionAbout->setStatusTip(QCoreApplication::translate("MainWindow", "About this emulator", nullptr));
#endif // QT_CONFIG(statustip)
actionOpen->setText(QCoreApplication::translate("MainWindow", "Open...", nullptr));
#if QT_CONFIG(statustip)
actionOpen->setStatusTip(QCoreApplication::translate("MainWindow", "Open a ROM", nullptr));
#endif // QT_CONFIG(statustip)
#if QT_CONFIG(shortcut)
actionOpen->setShortcut(QCoreApplication::translate("MainWindow", "Ctrl+O", nullptr));
#endif // QT_CONFIG(shortcut)
actionExit->setText(QCoreApplication::translate("MainWindow", "Exit", nullptr));
#if QT_CONFIG(statustip)
actionExit->setStatusTip(QCoreApplication::translate("MainWindow", "Quit the emulator", nullptr));
#endif // QT_CONFIG(statustip)
actionPause->setText(QCoreApplication::translate("MainWindow", "Pause", nullptr));
#if QT_CONFIG(statustip)
actionPause->setStatusTip(QCoreApplication::translate("MainWindow", "Pause the emulation", nullptr));
#endif // QT_CONFIG(statustip)
actionReset->setText(QCoreApplication::translate("MainWindow", "Reset", nullptr));
#if QT_CONFIG(statustip)
actionReset->setStatusTip(QCoreApplication::translate("MainWindow", "Reset the emulation", nullptr));
#endif // QT_CONFIG(statustip)
actionStop->setText(QCoreApplication::translate("MainWindow", "Stop", nullptr));
#if QT_CONFIG(statustip)
actionStop->setStatusTip(QCoreApplication::translate("MainWindow", "Stop the emulation", nullptr));
#endif // QT_CONFIG(statustip)
actionSettings->setText(QCoreApplication::translate("MainWindow", "Settings", nullptr));
#if QT_CONFIG(tooltip)
actionSettings->setToolTip(QCoreApplication::translate("MainWindow", "Settings", nullptr));
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(statustip)
actionSettings->setStatusTip(QCoreApplication::translate("MainWindow", "Open the settings window", nullptr));
#endif // QT_CONFIG(statustip)
menuFile->setTitle(QCoreApplication::translate("MainWindow", "File", nullptr));
menuEmulation->setTitle(QCoreApplication::translate("MainWindow", "Emulation", nullptr));
menuAbout->setTitle(QCoreApplication::translate("MainWindow", "Help", nullptr));
} // retranslateUi
void MainWindow::ConnectSignalsToSlots() noexcept {
connect(actionOpen, &QAction::triggered, this, [this]() {
QString file_name = QFileDialog::getOpenFileName( QString file_name = QFileDialog::getOpenFileName(
this, "Nintendo 64 executable", QString(), this, "Nintendo 64 executable", QString(),
"All supported types (*.zip *.ZIP *.7z *.7Z *.rar *.RAR *.tar *.TAR *.n64 *.N64 *.v64 *.V64 *.z64 *.Z64)"); "All supported types (*.zip *.ZIP *.7z *.7Z *.rar *.RAR *.tar *.TAR *.n64 *.N64 *.v64 *.V64 *.z64 *.Z64)");
if (!file_name.isEmpty()) { if (!file_name.isEmpty()) {
emit OpenROM(file_name); emit OpenROM(file_name);
view.vulkanWidget->show(); vulkanWidget->show();
} }
}); });
connect(view.actionExit, &QAction::triggered, this, [this]() { emit Exit(); }); connect(actionExit, &QAction::triggered, this, [this]() { emit Exit(); });
connect(this, &MainWindowController::destroyed, this, [this]() { emit Exit(); }); connect(this, &MainWindow::destroyed, this, [this]() { emit Exit(); });
connect(view.actionReset, &QAction::triggered, this, [this]() { emit Reset(); }); connect(actionReset, &QAction::triggered, this, [this]() { emit Reset(); });
connect(view.actionStop, &QAction::triggered, this, [this]() { connect(actionStop, &QAction::triggered, this, [this]() {
view.vulkanWidget->hide(); vulkanWidget->hide();
view.actionPause->setDisabled(true); actionPause->setDisabled(true);
view.actionReset->setDisabled(true); actionReset->setDisabled(true);
view.actionStop->setDisabled(true); actionStop->setDisabled(true);
emit Stop(); emit Stop();
}); });
connect(view.actionPause, &QAction::triggered, this, [this]() { connect(actionPause, &QAction::triggered, this, [this]() {
textPauseToggle = !textPauseToggle; textPauseToggle = !textPauseToggle;
view.actionPause->setText(textPauseToggle ? "Resume" : "Pause"); actionPause->setText(textPauseToggle ? "Resume" : "Pause");
emit Pause(); emit Pause();
}); });
connect(view.actionAbout, &QAction::triggered, this, [this]() { connect(actionAbout, &QAction::triggered, this, [this]() {
QMessageBox::about(this, tr("About Kaizen"), QMessageBox::about(this, tr("About Kaizen"),
tr("Kaizen is a Nintendo 64 emulator that strives to offer a friendly user " tr("Kaizen is a Nintendo 64 emulator that strives to offer a friendly user "
"experience and great compatibility.\n" "experience and great compatibility.\n"
@@ -53,5 +161,5 @@ void MainWindowController::ConnectSignalsToSlots() noexcept {
"Nintendo 64 is a registered trademarks of Nintendo Co., Ltd.")); "Nintendo 64 is a registered trademarks of Nintendo Co., Ltd."));
}); });
connect(view.actionSettings, &QAction::triggered, this, [this]() { emit OpenSettings(); }); connect(actionSettings, &QAction::triggered, this, [this]() { emit OpenSettings(); });
} }

View File

@@ -1,18 +1,36 @@
#pragma once #pragma once
#include <QApplication> #include <QApplication>
#include <QMainWindow> #include <QMainWindow>
#include <QVBoxLayout>
#include <RenderWidget.hpp> #include <RenderWidget.hpp>
#include "ui_mainwindow.h" #include <ImGuiWidget.hpp>
class MainWindowController : public QMainWindow { class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
public: public:
MainWindowController() noexcept; MainWindow() noexcept;
Ui::MainWindow view; QAction *actionAbout;
QAction *actionOpen;
QAction *actionExit;
QAction *actionPause;
QAction *actionReset;
QAction *actionStop;
QAction *actionSettings;
QWidget *centralwidget;
QVBoxLayout *verticalLayout;
QHBoxLayout *horizontalLayout;
RenderWidget *vulkanWidget;
ImGuiWidget *debugger;
QMenuBar *menubar;
QMenu *menuFile;
QMenu *menuEmulation;
QMenu *menuAbout;
QStatusBar *statusbar;
private: private:
void Retranslate();
void ConnectSignalsToSlots() noexcept; void ConnectSignalsToSlots() noexcept;
bool textPauseToggle = false; bool textPauseToggle = false;

View File

@@ -1,12 +1,9 @@
#pragma once #pragma once
#undef signals #undef signals
#include <ParallelRDPWrapper.hpp> #include <ParallelRDPWrapper.hpp>
#include <QVulkanInstance>
#include <QVulkanWindow> #include <QVulkanWindow>
#include <QWidget> #include <QWidget>
#include <QWindow> #include <QWindow>
#include <QTimer>
#include <imgui_impl_vulkan.h>
struct QtInstanceFactory : Vulkan::InstanceFactory { struct QtInstanceFactory : Vulkan::InstanceFactory {
VkInstance create_instance(const VkInstanceCreateInfo *info) override { VkInstance create_instance(const VkInstanceCreateInfo *info) override {

View File

@@ -1,146 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>646</height>
</rect>
</property>
<property name="windowTitle">
<string>Kaizen</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="RenderWidget" name="vulkanWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuEmulation">
<property name="title">
<string>Emulation</string>
</property>
<addaction name="actionSettings"/>
<addaction name="actionPause"/>
<addaction name="actionReset"/>
<addaction name="actionStop"/>
</widget>
<widget class="QMenu" name="menuAbout">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEmulation"/>
<addaction name="menuAbout"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionAbout">
<property name="text">
<string>About Kaizen</string>
</property>
<property name="statusTip">
<string>About this emulator</string>
</property>
</action>
<action name="actionOpen">
<property name="text">
<string>Open...</string>
</property>
<property name="statusTip">
<string>Open a ROM</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
<property name="statusTip">
<string>Quit the emulator</string>
</property>
</action>
<action name="actionPause">
<property name="text">
<string>Pause</string>
</property>
<property name="statusTip">
<string>Pause the emulation</string>
</property>
</action>
<action name="actionReset">
<property name="text">
<string>Reset</string>
</property>
<property name="statusTip">
<string>Reset the emulation</string>
</property>
</action>
<action name="actionStop">
<property name="text">
<string>Stop</string>
</property>
<property name="statusTip">
<string>Stop the emulation</string>
</property>
</action>
<action name="actionSettings">
<property name="text">
<string>Settings</string>
</property>
<property name="toolTip">
<string>Settings</string>
</property>
<property name="statusTip">
<string>Open the settings window</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>RenderWidget</class>
<extends>QWidget</extends>
<header>RenderWidget.hpp</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>