Some progress
This commit is contained in:
2
external/imgui/CMakeLists.txt
vendored
2
external/imgui/CMakeLists.txt
vendored
@@ -7,5 +7,3 @@ 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)
|
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
444
src/frontend/ImGuiWidget.cpp
Normal file
444
src/frontend/ImGuiWidget.cpp
Normal 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
|
||||||
|
}
|
||||||
43
src/frontend/ImGuiWidget.hpp
Normal file
43
src/frontend/ImGuiWidget.hpp
Normal 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;
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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(); });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
|
||||||
Reference in New Issue
Block a user