From 42d14eec96c6e63ea93cfb7daa49655b7139c997 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Mon, 18 Mar 2024 09:18:02 +0100 Subject: [PATCH 01/10] Minor code improvements (#1124) --- .../LatteDecompilerEmitGLSL.cpp | 2 +- .../HW/Latte/Renderer/OpenGL/OpenGLRenderer.h | 2 -- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 10 +++++----- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 3 +-- src/input/motion/MotionSample.h | 20 +++++++++---------- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp index f3d2c7a8..e19535be 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp @@ -973,7 +973,7 @@ void _emitOperandInputCode(LatteDecompilerShaderContext* shaderContext, LatteDec } else { - cemuLog_log(LogType::Force, "Unsupported shader ALU operand sel 0x%x\n", aluInstruction->sourceOperand[operandIndex].sel); + cemuLog_log(LogType::Force, "Unsupported shader ALU operand sel {:#x}\n", aluInstruction->sourceOperand[operandIndex].sel); debugBreakpoint(); } diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h index 3a892191..313ea3c0 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h @@ -30,8 +30,6 @@ public: void Flush(bool waitIdle = false) override; void NotifyLatteCommandProcessorIdle() override; - void UpdateVSyncState(); - void EnableDebugMode() override; void SwapBuffers(bool swapTV = true, bool swapDRC = true) override; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index d62b61a6..02bb1e71 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -706,8 +706,8 @@ SwapchainInfoVk& VulkanRenderer::GetChainInfo(bool mainWindow) const void VulkanRenderer::StopUsingPadAndWait() { - m_destroyPadSwapchainNextAcquire = true; - m_padCloseReadySemaphore.wait(); + m_destroyPadSwapchainNextAcquire.test_and_set(); + m_destroyPadSwapchainNextAcquire.wait(true); } bool VulkanRenderer::IsPadWindowActive() @@ -2557,11 +2557,11 @@ bool VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow) if(!IsSwapchainInfoValid(mainWindow)) return false; - if(!mainWindow && m_destroyPadSwapchainNextAcquire) + if(!mainWindow && m_destroyPadSwapchainNextAcquire.test()) { RecreateSwapchain(mainWindow, true); - m_destroyPadSwapchainNextAcquire = false; - m_padCloseReadySemaphore.notify(); + m_destroyPadSwapchainNextAcquire.clear(); + m_destroyPadSwapchainNextAcquire.notify_all(); return false; } diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index e0a4c75b..47097dfa 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -414,8 +414,7 @@ private: }m_state; std::unique_ptr m_mainSwapchainInfo{}, m_padSwapchainInfo{}; - Semaphore m_padCloseReadySemaphore; - bool m_destroyPadSwapchainNextAcquire = false; + std::atomic_flag m_destroyPadSwapchainNextAcquire{}; bool IsSwapchainInfoValid(bool mainWindow) const; VkRenderPass m_imguiRenderPass = VK_NULL_HANDLE; diff --git a/src/input/motion/MotionSample.h b/src/input/motion/MotionSample.h index bb47f784..0697711b 100644 --- a/src/input/motion/MotionSample.h +++ b/src/input/motion/MotionSample.h @@ -258,48 +258,48 @@ DRC flat on table, screen facing up. Top pointing away (away from person, pointi 0.03 0.99 -0.13 0.01 0.13 0.99 -Turned 45° to the right: +Turned 45° to the right: 0.71 -0.03 0.71 0.12 0.99 -0.08 -0.70 0.14 0.70 -Turned 45° to the right (top of GamePad pointing right now): +Turned 45° to the right (top of GamePad pointing right now): 0.08 -0.03 1.00 -> Z points towards person 0.15 0.99 0.01 -0.99 0.15 0.09 -> DRC Z-Axis now points towards X-minus -Turned 90° to the right (top of gamepad now pointing towards holder, away from monitor): +Turned 90° to the right (top of gamepad now pointing towards holder, away from monitor): -1.00 -0.01 0.06 0.00 0.99 0.15 -0.06 0.15 -0.99 -Turned 90° to the right (pointing left): +Turned 90° to the right (pointing left): -0.17 -0.01 -0.99 -0.13 0.99 0.02 0.98 0.13 -0.17 -After another 90° we end up in the initial position: +After another 90° we end up in the initial position: 0.99 -0.03 -0.11 0.01 0.99 -0.13 0.12 0.12 0.99 ------ -From initial position, lean the GamePad on its left side. 45° up. So the screen is pointing to the top left +From initial position, lean the GamePad on its left side. 45° up. So the screen is pointing to the top left 0.66 -0.75 -0.03 0.74 0.66 -0.11 0.10 0.05 0.99 -Further 45°, GamePad now on its left, screen pointing left: +Further 45°, GamePad now on its left, screen pointing left: -0.03 -1.00 -0.00 0.99 -0.03 -0.15 0.15 -0.01 0.99 -From initial position, lean the GamePad on its right side. 45° up. So the screen is pointing to the top right +From initial position, lean the GamePad on its right side. 45° up. So the screen is pointing to the top right 0.75 0.65 -0.11 -0.65 0.76 0.07 0.12 0.02 0.99 -From initial position, tilt the GamePad up 90° (bottom side remains in touch with surface): +From initial position, tilt the GamePad up 90° (bottom side remains in touch with surface): 0.99 -0.05 -0.10 -0.10 0.01 -0.99 0.05 1.00 0.01 @@ -309,7 +309,7 @@ From initial position, stand the GamePad on its top side: 0.09 -0.01 1.00 -0.01 -1.00 -0.01 -Rotate GamePad 180° around x axis, so it now lies on its screen (top of GamePad pointing to holder): +Rotate GamePad 180° around x axis, so it now lies on its screen (top of GamePad pointing to holder): 0.99 -0.03 -0.15 -0.04 -1.00 -0.08 -0.15 0.09 -0.99 From 4d609f06b810a6bc686aa783a2b716e3cd280f0e Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:22:48 +0100 Subject: [PATCH 02/10] InputSettings: Fix controller type counter to restore WPAD limit (#1118) --- src/gui/input/InputSettings2.cpp | 32 ++++---------------------------- src/gui/input/InputSettings2.h | 3 --- 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/src/gui/input/InputSettings2.cpp b/src/gui/input/InputSettings2.cpp index 58c168a3..72bf4f7d 100644 --- a/src/gui/input/InputSettings2.cpp +++ b/src/gui/input/InputSettings2.cpp @@ -295,32 +295,6 @@ wxWindow* InputSettings2::initialize_page(size_t index) return page; } -std::pair InputSettings2::get_emulated_controller_types() const -{ - size_t vpad = 0, wpad = 0; - for(size_t i = 0; i < m_notebook->GetPageCount(); ++i) - { - auto* page = m_notebook->GetPage(i); - auto* page_data = (wxControllerPageData*)page->GetClientObject(); - if (!page_data) - continue; - - if (!page_data->ref().m_controller) // = disabled - continue; - - const auto api_type = page_data->ref().m_controller->type(); - if (api_type) - continue; - - if (api_type == EmulatedController::VPAD) - ++vpad; - else - ++wpad; - } - - return std::make_pair(vpad, wpad); -} - std::shared_ptr InputSettings2::get_active_controller() const { auto& page_data = get_current_page_data(); @@ -771,14 +745,16 @@ void InputSettings2::on_emulated_controller_dropdown(wxCommandEvent& event) wxWindowUpdateLocker lock(emulated_controllers); bool is_gamepad_selected = false; + bool is_wpad_selected = false; const auto selected = emulated_controllers->GetSelection(); const auto selected_value = emulated_controllers->GetStringSelection(); if(selected != wxNOT_FOUND) { is_gamepad_selected = selected_value == to_wxString(EmulatedController::type_to_string(EmulatedController::Type::VPAD)); + is_wpad_selected = !is_gamepad_selected && selected != 0; } - const auto [vpad_count, wpad_count] = get_emulated_controller_types(); + const auto [vpad_count, wpad_count] = InputManager::instance().get_controller_count(); emulated_controllers->Clear(); emulated_controllers->AppendString(_("Disabled")); @@ -786,7 +762,7 @@ void InputSettings2::on_emulated_controller_dropdown(wxCommandEvent& event) if (vpad_count < InputManager::kMaxVPADControllers || is_gamepad_selected) emulated_controllers->Append(to_wxString(EmulatedController::type_to_string(EmulatedController::Type::VPAD))); - if (wpad_count < InputManager::kMaxWPADControllers || !is_gamepad_selected) + if (wpad_count < InputManager::kMaxWPADControllers || is_wpad_selected) { emulated_controllers->AppendString(to_wxString(EmulatedController::type_to_string(EmulatedController::Type::Pro))); emulated_controllers->AppendString(to_wxString(EmulatedController::type_to_string(EmulatedController::Type::Classic))); diff --git a/src/gui/input/InputSettings2.h b/src/gui/input/InputSettings2.h index 01313653..1a3c8bb1 100644 --- a/src/gui/input/InputSettings2.h +++ b/src/gui/input/InputSettings2.h @@ -27,9 +27,6 @@ private: wxWindow* initialize_page(size_t index); - // count active controllers - std::pair get_emulated_controller_types() const; - // currently selected controller from active tab std::shared_ptr get_active_controller() const; From 17060752b6d5bc952446e26c4cd4b0d259653ced Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Sun, 24 Mar 2024 10:57:08 +0100 Subject: [PATCH 03/10] Vulkan: Several swapchain fixes and refactors (#1132) --- .../Latte/Renderer/Vulkan/SwapchainInfoVk.cpp | 91 +++++++++---------- .../Latte/Renderer/Vulkan/SwapchainInfoVk.h | 25 ++--- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 54 ++++++++--- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 13 ++- 4 files changed, 99 insertions(+), 84 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp index 14b7a17c..b00f5490 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp @@ -1,15 +1,34 @@ #include "SwapchainInfoVk.h" #include "config/CemuConfig.h" +#include "gui/guiWrapper.h" #include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/HW/Latte/Core/LatteTiming.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" +#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" -void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDevice) +SwapchainInfoVk::SwapchainInfoVk(bool mainWindow, Vector2i size) : mainWindow(mainWindow), m_desiredExtent(size) { - m_physicalDevice = physicalDevice; - m_logicalDevice = logicalDevice; - const auto details = QuerySwapchainSupport(surface, physicalDevice); + auto& windowHandleInfo = mainWindow ? gui_getWindowInfo().canvas_main : gui_getWindowInfo().canvas_pad; + auto renderer = VulkanRenderer::GetInstance(); + m_instance = renderer->GetVkInstance(); + m_logicalDevice = renderer->GetLogicalDevice(); + m_physicalDevice = renderer->GetPhysicalDevice(); + + m_surface = renderer->CreateFramebufferSurface(m_instance, windowHandleInfo); +} + + +SwapchainInfoVk::~SwapchainInfoVk() +{ + Cleanup(); + if(m_surface != VK_NULL_HANDLE) + vkDestroySurfaceKHR(m_instance, m_surface, nullptr); +} + +void SwapchainInfoVk::Create() +{ + const auto details = QuerySwapchainSupport(m_surface, m_physicalDevice); m_surfaceFormat = ChooseSurfaceFormat(details.formats); m_actualExtent = ChooseSwapExtent(details.capabilities); @@ -20,28 +39,28 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe if(image_count < 2) cemuLog_log(LogType::Force, "Vulkan: Swapchain image count less than 2 may cause problems"); - VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(surface, details, m_surfaceFormat, image_count, m_actualExtent); + VkSwapchainCreateInfoKHR create_info = CreateSwapchainCreateInfo(m_surface, details, m_surfaceFormat, image_count, m_actualExtent); create_info.oldSwapchain = nullptr; create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - VkResult result = vkCreateSwapchainKHR(logicalDevice, &create_info, nullptr, &swapchain); + VkResult result = vkCreateSwapchainKHR(m_logicalDevice, &create_info, nullptr, &m_swapchain); if (result != VK_SUCCESS) UnrecoverableError("Error attempting to create a swapchain"); - result = vkGetSwapchainImagesKHR(logicalDevice, swapchain, &image_count, nullptr); + result = vkGetSwapchainImagesKHR(m_logicalDevice, m_swapchain, &image_count, nullptr); if (result != VK_SUCCESS) UnrecoverableError("Error attempting to retrieve the count of swapchain images"); m_swapchainImages.resize(image_count); - result = vkGetSwapchainImagesKHR(logicalDevice, swapchain, &image_count, m_swapchainImages.data()); + result = vkGetSwapchainImagesKHR(m_logicalDevice, m_swapchain, &image_count, m_swapchainImages.data()); if (result != VK_SUCCESS) UnrecoverableError("Error attempting to retrieve swapchain images"); // create default renderpass VkAttachmentDescription colorAttachment = {}; colorAttachment.format = m_surfaceFormat.format; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; @@ -62,7 +81,7 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe renderPassInfo.pAttachments = &colorAttachment; renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; - result = vkCreateRenderPass(logicalDevice, &renderPassInfo, nullptr, &m_swapchainRenderPass); + result = vkCreateRenderPass(m_logicalDevice, &renderPassInfo, nullptr, &m_swapchainRenderPass); if (result != VK_SUCCESS) UnrecoverableError("Failed to create renderpass for swapchain"); @@ -84,7 +103,7 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe createInfo.subresourceRange.levelCount = 1; createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - result = vkCreateImageView(logicalDevice, &createInfo, nullptr, &m_swapchainImageViews[i]); + result = vkCreateImageView(m_logicalDevice, &createInfo, nullptr, &m_swapchainImageViews[i]); if (result != VK_SUCCESS) UnrecoverableError("Failed to create imageviews for swapchain"); } @@ -104,7 +123,7 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe framebufferInfo.width = m_actualExtent.width; framebufferInfo.height = m_actualExtent.height; framebufferInfo.layers = 1; - result = vkCreateFramebuffer(logicalDevice, &framebufferInfo, nullptr, &m_swapchainFramebuffers[i]); + result = vkCreateFramebuffer(m_logicalDevice, &framebufferInfo, nullptr, &m_swapchainFramebuffers[i]); if (result != VK_SUCCESS) UnrecoverableError("Failed to create framebuffer for swapchain"); } @@ -114,7 +133,7 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe VkSemaphoreCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; for (auto& semaphore : m_presentSemaphores){ - if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) + if (vkCreateSemaphore(m_logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) UnrecoverableError("Failed to create semaphore for swapchain present"); } @@ -123,14 +142,14 @@ void SwapchainInfoVk::Create(VkPhysicalDevice physicalDevice, VkDevice logicalDe info = {}; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; for (auto& semaphore : m_acquireSemaphores){ - if (vkCreateSemaphore(logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) + if (vkCreateSemaphore(m_logicalDevice, &info, nullptr, &semaphore) != VK_SUCCESS) UnrecoverableError("Failed to create semaphore for swapchain acquire"); } VkFenceCreateInfo fenceInfo = {}; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - result = vkCreateFence(logicalDevice, &fenceInfo, nullptr, &m_imageAvailableFence); + result = vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &m_imageAvailableFence); if (result != VK_SUCCESS) UnrecoverableError("Failed to create fence for swapchain"); @@ -167,19 +186,20 @@ void SwapchainInfoVk::Cleanup() if (m_imageAvailableFence) { + WaitAvailableFence(); vkDestroyFence(m_logicalDevice, m_imageAvailableFence, nullptr); m_imageAvailableFence = nullptr; } - if (swapchain) + if (m_swapchain) { - vkDestroySwapchainKHR(m_logicalDevice, swapchain, nullptr); - swapchain = VK_NULL_HANDLE; + vkDestroySwapchainKHR(m_logicalDevice, m_swapchain, nullptr); + m_swapchain = VK_NULL_HANDLE; } } bool SwapchainInfoVk::IsValid() const { - return swapchain && !m_acquireSemaphores.empty(); + return m_swapchain && !m_acquireSemaphores.empty(); } void SwapchainInfoVk::WaitAvailableFence() @@ -207,7 +227,7 @@ bool SwapchainInfoVk::AcquireImage(uint64 timeout) ResetAvailableFence(); VkSemaphore acquireSemaphore = m_acquireSemaphores[m_acquireIndex]; - VkResult result = vkAcquireNextImageKHR(m_logicalDevice, swapchain, timeout, acquireSemaphore, m_imageAvailableFence, &swapchainImageIndex); + VkResult result = vkAcquireNextImageKHR(m_logicalDevice, m_swapchain, timeout, acquireSemaphore, m_imageAvailableFence, &swapchainImageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) m_shouldRecreate = true; if (result < 0) @@ -231,35 +251,6 @@ void SwapchainInfoVk::UnrecoverableError(const char* errMsg) throw std::runtime_error(errMsg); } -SwapchainInfoVk::QueueFamilyIndices SwapchainInfoVk::FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device) -{ - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - QueueFamilyIndices indices; - for (int i = 0; i < (int)queueFamilies.size(); ++i) - { - const auto& queueFamily = queueFamilies[i]; - if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) - indices.graphicsFamily = i; - - VkBool32 presentSupport = false; - const VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (result != VK_SUCCESS) - throw std::runtime_error(fmt::format("Error while attempting to check if a surface supports presentation: {}", result)); - - if (queueFamily.queueCount > 0 && presentSupport) - indices.presentFamily = i; - - if (indices.IsComplete()) - break; - } - - return indices; -} SwapchainInfoVk::SwapchainSupportDetails SwapchainInfoVk::QuerySwapchainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device) { @@ -391,7 +382,7 @@ VkSwapchainCreateInfoKHR SwapchainInfoVk::CreateSwapchainCreateInfo(VkSurfaceKHR createInfo.imageArrayLayers = 1; createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - const QueueFamilyIndices indices = FindQueueFamilies(surface, m_physicalDevice); + const VulkanRenderer::QueueFamilyIndices indices = VulkanRenderer::GetInstance()->FindQueueFamilies(surface, m_physicalDevice); m_swapchainQueueFamilyIndices = { (uint32)indices.graphicsFamily, (uint32)indices.presentFamily }; if (indices.graphicsFamily != indices.presentFamily) { diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h index 26dbc7d1..0e8c2ade 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h @@ -14,14 +14,6 @@ struct SwapchainInfoVk SYNC_AND_LIMIT = 3, // synchronize emulated vsync events to monitor vsync. But skip events if rate higher than virtual vsync period }; - struct QueueFamilyIndices - { - int32_t graphicsFamily = -1; - int32_t presentFamily = -1; - - bool IsComplete() const { return graphicsFamily >= 0 && presentFamily >= 0; } - }; - struct SwapchainSupportDetails { VkSurfaceCapabilitiesKHR capabilities; @@ -30,7 +22,7 @@ struct SwapchainInfoVk }; void Cleanup(); - void Create(VkPhysicalDevice physicalDevice, VkDevice logicalDevice); + void Create(); bool IsValid() const; @@ -45,8 +37,6 @@ struct SwapchainInfoVk static void UnrecoverableError(const char* errMsg); - // todo: move this function somewhere more sensible. Not directly swapchain related - static QueueFamilyIndices FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device); static SwapchainSupportDetails QuerySwapchainSupport(VkSurfaceKHR surface, const VkPhysicalDevice& device); VkPresentModeKHR ChoosePresentMode(const std::vector& modes); @@ -61,14 +51,10 @@ struct SwapchainInfoVk return m_actualExtent; } - SwapchainInfoVk(VkSurfaceKHR surface, bool mainWindow) - : surface(surface), mainWindow(mainWindow) {} + SwapchainInfoVk(bool mainWindow, Vector2i size); SwapchainInfoVk(const SwapchainInfoVk&) = delete; SwapchainInfoVk(SwapchainInfoVk&&) noexcept = default; - ~SwapchainInfoVk() - { - Cleanup(); - } + ~SwapchainInfoVk(); bool mainWindow{}; @@ -77,11 +63,12 @@ struct SwapchainInfoVk VSync m_vsyncState = VSync::Immediate; bool hasDefinedSwapchainImage{}; // indicates if the swapchain image is in a defined state + VkInstance m_instance{}; VkPhysicalDevice m_physicalDevice{}; VkDevice m_logicalDevice{}; - VkSurfaceKHR surface{}; + VkSurfaceKHR m_surface{}; VkSurfaceFormatKHR m_surfaceFormat{}; - VkSwapchainKHR swapchain{}; + VkSwapchainKHR m_swapchain{}; Vector2i m_desiredExtent{}; uint32 swapchainImageIndex = (uint32)-1; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 02bb1e71..e0ebda2a 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -167,6 +167,7 @@ std::vector VulkanRenderer::GetDevices() result.emplace_back(physDeviceProps.properties.deviceName, physDeviceIDProps.deviceUUID); } } + vkDestroySurfaceKHR(instance, surface, nullptr); } catch (...) { @@ -441,7 +442,7 @@ VulkanRenderer::VulkanRenderer() } // create logical device - m_indices = SwapchainInfoVk::FindQueueFamilies(surface, m_physicalDevice); + m_indices = FindQueueFamilies(surface, m_physicalDevice); std::set uniqueQueueFamilies = { m_indices.graphicsFamily, m_indices.presentFamily }; std::vector queueCreateInfos = CreateQueueCreateInfos(uniqueQueueFamilies); VkPhysicalDeviceFeatures deviceFeatures = {}; @@ -510,7 +511,7 @@ VulkanRenderer::VulkanRenderer() PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkCreateDebugUtilsMessengerEXT")); VkDebugUtilsMessengerCreateInfoEXT debugCallback{}; - debugCallback.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + debugCallback.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; debugCallback.pNext = nullptr; debugCallback.flags = 0; debugCallback.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; @@ -673,24 +674,19 @@ VulkanRenderer* VulkanRenderer::GetInstance() void VulkanRenderer::InitializeSurface(const Vector2i& size, bool mainWindow) { - auto& windowHandleInfo = mainWindow ? gui_getWindowInfo().canvas_main : gui_getWindowInfo().canvas_pad; - - const auto surface = CreateFramebufferSurface(m_instance, windowHandleInfo); if (mainWindow) { - m_mainSwapchainInfo = std::make_unique(surface, mainWindow); - m_mainSwapchainInfo->m_desiredExtent = size; - m_mainSwapchainInfo->Create(m_physicalDevice, m_logicalDevice); + m_mainSwapchainInfo = std::make_unique(mainWindow, size); + m_mainSwapchainInfo->Create(); // aquire first command buffer InitFirstCommandBuffer(); } else { - m_padSwapchainInfo = std::make_unique(surface, mainWindow); - m_padSwapchainInfo->m_desiredExtent = size; + m_padSwapchainInfo = std::make_unique(mainWindow, size); // todo: figure out a way to exclusively create swapchain on main LatteThread - m_padSwapchainInfo->Create(m_physicalDevice, m_logicalDevice); + m_padSwapchainInfo->Create(); } } @@ -1074,6 +1070,36 @@ RendererShader* VulkanRenderer::shader_create(RendererShader::ShaderType type, u return new RendererShaderVk(type, baseHash, auxHash, isGameShader, isGfxPackShader, source); } +VulkanRenderer::QueueFamilyIndices VulkanRenderer::FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device) +{ + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + QueueFamilyIndices indices; + for (int i = 0; i < (int)queueFamilies.size(); ++i) + { + const auto& queueFamily = queueFamilies[i]; + if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) + indices.graphicsFamily = i; + + VkBool32 presentSupport = false; + const VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + if (result != VK_SUCCESS) + throw std::runtime_error(fmt::format("Error while attempting to check if a surface supports presentation: {}", result)); + + if (queueFamily.queueCount > 0 && presentSupport) + indices.presentFamily = i; + + if (indices.IsComplete()) + break; + } + + return indices; +} + bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device, FeatureControl& info) { std::vector availableDeviceExtensions; @@ -1215,7 +1241,7 @@ std::vector VulkanRenderer::CheckInstanceExtensionSupport(FeatureCo bool VulkanRenderer::IsDeviceSuitable(VkSurfaceKHR surface, const VkPhysicalDevice& device) { - if (!SwapchainInfoVk::FindQueueFamilies(surface, device).IsComplete()) + if (!FindQueueFamilies(surface, device).IsComplete()) return false; // check API version (using Vulkan 1.0 way of querying properties) @@ -2605,7 +2631,7 @@ void VulkanRenderer::RecreateSwapchain(bool mainWindow, bool skipCreate) chainInfo.m_desiredExtent = size; if(!skipCreate) { - chainInfo.Create(m_physicalDevice, m_logicalDevice); + chainInfo.Create(); } if (mainWindow) @@ -2675,7 +2701,7 @@ void VulkanRenderer::SwapBuffer(bool mainWindow) VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &chainInfo.swapchain; + presentInfo.pSwapchains = &chainInfo.m_swapchain; presentInfo.pImageIndices = &chainInfo.swapchainImageIndex; // wait on command buffer semaphore presentInfo.waitSemaphoreCount = 1; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 47097dfa..2491d052 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -127,7 +127,6 @@ class VulkanRenderer : public Renderer friend class PipelineCompiler; using VSync = SwapchainInfoVk::VSync; - using QueueFamilyIndices = SwapchainInfoVk::QueueFamilyIndices; static const inline int UNIFORMVAR_RINGBUFFER_SIZE = 1024 * 1024 * 16; // 16MB @@ -421,6 +420,18 @@ private: VkDescriptorPool m_descriptorPool; + public: + struct QueueFamilyIndices + { + int32_t graphicsFamily = -1; + int32_t presentFamily = -1; + + bool IsComplete() const { return graphicsFamily >= 0 && presentFamily >= 0; } + }; + static QueueFamilyIndices FindQueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device); + + private: + struct FeatureControl { struct From 241915e1a6bfd92e4ffd0d6961a178335300e83f Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Sun, 24 Mar 2024 10:11:18 +0000 Subject: [PATCH 04/10] Gamelist: Display title long names + improvements for shortcuts (#1126) - Windows icons are stored as .ico files to %LOCALAPPDATA%/Cemu/icons/ - Long title names chosen as some games (NSMBU + NSLU) add trailing dots for their shortnames - Long title names have their newlines replaced with spaces at parsing - Linux shortcut paths are saved with UTF-8 encoding - Game titles are copied and saved with UTF-8 encoding --- src/Cafe/TitleList/ParsedMetaXml.h | 7 +- src/Cafe/TitleList/TitleInfo.cpp | 4 +- src/gui/CemuApp.cpp | 7 +- src/gui/components/wxGameList.cpp | 272 ++++++++++++++++------------- 4 files changed, 168 insertions(+), 122 deletions(-) diff --git a/src/Cafe/TitleList/ParsedMetaXml.h b/src/Cafe/TitleList/ParsedMetaXml.h index 7537d603..f1aee37e 100644 --- a/src/Cafe/TitleList/ParsedMetaXml.h +++ b/src/Cafe/TitleList/ParsedMetaXml.h @@ -90,8 +90,11 @@ struct ParsedMetaXml else if (boost::starts_with(name, "longname_")) { const sint32 index = GetLanguageIndex(name.substr(std::size("longname_") - 1)); - if (index != -1) - parsedMetaXml->m_long_name[index] = child.text().as_string(); + if (index != -1){ + std::string longname = child.text().as_string(); + std::replace_if(longname.begin(), longname.end(), [](char c) { return c == '\r' || c == '\n';}, ' '); + parsedMetaXml->m_long_name[index] = longname; + } } else if (boost::starts_with(name, L"shortname_")) { diff --git a/src/Cafe/TitleList/TitleInfo.cpp b/src/Cafe/TitleList/TitleInfo.cpp index ff457575..d23e1d0a 100644 --- a/src/Cafe/TitleList/TitleInfo.cpp +++ b/src/Cafe/TitleList/TitleInfo.cpp @@ -637,9 +637,9 @@ std::string TitleInfo::GetMetaTitleName() const if (m_parsedMetaXml) { std::string titleNameCfgLanguage; - titleNameCfgLanguage = m_parsedMetaXml->GetShortName(GetConfig().console_language); + titleNameCfgLanguage = m_parsedMetaXml->GetLongName(GetConfig().console_language); if (titleNameCfgLanguage.empty()) //Get English Title - titleNameCfgLanguage = m_parsedMetaXml->GetShortName(CafeConsoleLanguage::EN); + titleNameCfgLanguage = m_parsedMetaXml->GetLongName(CafeConsoleLanguage::EN); if (titleNameCfgLanguage.empty()) //Unknown Title titleNameCfgLanguage = "Unknown Title"; return titleNameCfgLanguage; diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index fde4bcc0..7f11d4c6 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -59,7 +59,12 @@ bool CemuApp::OnInit() fs::path user_data_path, config_path, cache_path, data_path; auto standardPaths = wxStandardPaths::Get(); fs::path exePath(wxHelper::MakeFSPath(standardPaths.GetExecutablePath())); - +#if BOOST_OS_LINUX + // GetExecutablePath returns the AppImage's temporary mount location + wxString appImagePath; + if (wxGetEnv(("APPIMAGE"), &appImagePath)) + exePath = wxHelper::MakeFSPath(appImagePath); +#endif // Try a portable path first, if it exists. user_data_path = config_path = cache_path = data_path = exePath.parent_path() / "portable"; #if BOOST_OS_MACOS diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index 8e8f3c4d..73bcd98d 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -19,7 +19,6 @@ #include #include - #include #include @@ -526,7 +525,6 @@ void wxGameList::OnKeyDown(wxListEvent& event) } } - enum ContextMenuEntries { kContextMenuRefreshGames = wxID_HIGHEST + 1, @@ -732,7 +730,7 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event) { if (wxTheClipboard->Open()) { - wxTheClipboard->SetData(new wxTextDataObject(gameInfo.GetTitleName())); + wxTheClipboard->SetData(new wxTextDataObject(wxString::FromUTF8(gameInfo.GetTitleName()))); wxTheClipboard->Close(); } break; @@ -1276,129 +1274,169 @@ void wxGameList::DeleteCachedStrings() m_name_cache.clear(); } -#if BOOST_OS_LINUX || BOOST_OS_WINDOWS -void wxGameList::CreateShortcut(GameInfo2& gameInfo) { - const auto title_id = gameInfo.GetBaseTitleId(); - const auto title_name = gameInfo.GetTitleName(); - auto exe_path = ActiveSettings::GetExecutablePath(); - const char *flatpak_id = getenv("FLATPAK_ID"); - - // GetExecutablePath returns the AppImage's temporary mount location, instead of its actual path - wxString appimage_path; - if (wxGetEnv(("APPIMAGE"), &appimage_path)) { - exe_path = appimage_path.utf8_string(); - } - #if BOOST_OS_LINUX - const wxString desktop_entry_name = wxString::Format("%s.desktop", title_name); - wxFileDialog entry_dialog(this, _("Choose desktop entry location"), "~/.local/share/applications", desktop_entry_name, - "Desktop file (*.desktop)|*.desktop", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT); -#elif BOOST_OS_WINDOWS - // Get '%APPDATA%\Microsoft\Windows\Start Menu\Programs' path - PWSTR user_shortcut_folder; - SHGetKnownFolderPath(FOLDERID_Programs, 0, NULL, &user_shortcut_folder); - const wxString shortcut_name = wxString::Format("%s.lnk", title_name); - wxFileDialog entry_dialog(this, _("Choose shortcut location"), _pathToUtf8(user_shortcut_folder), shortcut_name, - "Shortcut (*.lnk)|*.lnk", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT); -#endif - const auto result = entry_dialog.ShowModal(); - if (result == wxID_CANCEL) - return; - const auto output_path = entry_dialog.GetPath(); +void wxGameList::CreateShortcut(GameInfo2& gameInfo) +{ + const auto titleId = gameInfo.GetBaseTitleId(); + const auto titleName = wxString::FromUTF8(gameInfo.GetTitleName()); + auto exePath = ActiveSettings::GetExecutablePath(); + const char* flatpakId = getenv("FLATPAK_ID"); -#if BOOST_OS_LINUX - std::optional icon_path; - // Obtain and convert icon - { - m_icon_cache_mtx.lock(); - const auto icon_iter = m_icon_cache.find(title_id); - const auto result_index = (icon_iter != m_icon_cache.cend()) ? std::optional(icon_iter->second.first) : std::nullopt; - m_icon_cache_mtx.unlock(); + const wxString desktopEntryName = wxString::Format("%s.desktop", titleName); + wxFileDialog entryDialog(this, _("Choose desktop entry location"), "~/.local/share/applications", desktopEntryName, + "Desktop file (*.desktop)|*.desktop", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT); + const auto result = entryDialog.ShowModal(); + if (result == wxID_CANCEL) + return; + const auto output_path = entryDialog.GetPath(); - // In most cases it should find it - if (!result_index){ - wxMessageBox(_("Icon is yet to load, so will not be used by the shortcut"), _("Warning"), wxOK | wxCENTRE | wxICON_WARNING); - } - else { - const fs::path out_icon_dir = ActiveSettings::GetUserDataPath("icons"); + std::optional iconPath; + // Obtain and convert icon + [&]() + { + int iconIndex, smallIconIndex; - if (!fs::exists(out_icon_dir) && !fs::create_directories(out_icon_dir)){ - wxMessageBox(_("Cannot access the icon directory, the shortcut will have no icon"), _("Warning"), wxOK | wxCENTRE | wxICON_WARNING); - } - else { - icon_path = out_icon_dir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId()); + if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex)) + { + cemuLog_log(LogType::Force, "Icon hasn't loaded"); + return; + } + const fs::path outIconDir = ActiveSettings::GetUserDataPath("icons"); - auto image = m_image_list->GetIcon(result_index.value()).ConvertToImage(); + if (!fs::exists(outIconDir) && !fs::create_directories(outIconDir)) + { + cemuLog_log(LogType::Force, "Failed to create icon directory"); + return; + } - wxFileOutputStream png_file(_pathToUtf8(icon_path.value())); - wxPNGHandler pngHandler; - if (!pngHandler.SaveFile(&image, png_file, false)) { - icon_path = std::nullopt; - wxMessageBox(_("The icon was unable to be saved, the shortcut will have no icon"), _("Warning"), wxOK | wxCENTRE | wxICON_WARNING); - } - } - } - } + iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId()); + wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value())); - std::string desktop_exec_entry; - if (flatpak_id) - desktop_exec_entry = fmt::format("/usr/bin/flatpak run {0} --title-id {1:016x}", flatpak_id, title_id); - else - desktop_exec_entry = fmt::format("{0:?} --title-id {1:016x}", _pathToUtf8(exe_path), title_id); + auto image = m_image_list->GetIcon(iconIndex).ConvertToImage(); + wxPNGHandler pngHandler; + if (!pngHandler.SaveFile(&image, pngFileStream, false)) + { + iconPath = std::nullopt; + cemuLog_log(LogType::Force, "Icon failed to save"); + } + }(); - // 'Icon' accepts spaces in file name, does not accept quoted file paths - // 'Exec' does not accept non-escaped spaces, and can accept quoted file paths - auto desktop_entry_string = - fmt::format("[Desktop Entry]\n" - "Name={0}\n" - "Comment=Play {0} on Cemu\n" - "Exec={1}\n" - "Icon={2}\n" - "Terminal=false\n" - "Type=Application\n" - "Categories=Game;\n", - title_name, - desktop_exec_entry, - _pathToUtf8(icon_path.value_or(""))); + std::string desktopExecEntry = flatpakId ? fmt::format("/usr/bin/flatpak run {0} --title-id {1:016x}", flatpakId, titleId) + : fmt::format("{0:?} --title-id {1:016x}", _pathToUtf8(exePath), titleId); - if (flatpak_id) - desktop_entry_string += fmt::format("X-Flatpak={}\n", flatpak_id); + // 'Icon' accepts spaces in file name, does not accept quoted file paths + // 'Exec' does not accept non-escaped spaces, and can accept quoted file paths + auto desktopEntryString = fmt::format( + "[Desktop Entry]\n" + "Name={0}\n" + "Comment=Play {0} on Cemu\n" + "Exec={1}\n" + "Icon={2}\n" + "Terminal=false\n" + "Type=Application\n" + "Categories=Game;\n", + titleName.utf8_string(), + desktopExecEntry, + _pathToUtf8(iconPath.value_or(""))); - std::ofstream output_stream(output_path); - if (!output_stream.good()) - { - auto errorMsg = formatWxString(_("Failed to save desktop entry to {}"), output_path.utf8_string()); - wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return; - } - output_stream << desktop_entry_string; + if (flatpakId) + desktopEntryString += fmt::format("X-Flatpak={}\n", flatpakId); -#elif BOOST_OS_WINDOWS - IShellLinkW *shell_link; - HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast(&shell_link)); - if (SUCCEEDED(hres)) - { - const auto description = wxString::Format("Play %s on Cemu", title_name); - const auto args = wxString::Format("-t %016llx", title_id); - - shell_link->SetPath(exe_path.wstring().c_str()); - shell_link->SetDescription(description.wc_str()); - shell_link->SetArguments(args.wc_str()); - shell_link->SetWorkingDirectory(exe_path.parent_path().wstring().c_str()); - // Use icon from Cemu exe for now since we can't embed icons into the shortcut - // in the future we could convert and store icons in AppData or ProgramData - shell_link->SetIconLocation(exe_path.wstring().c_str(), 0); - - IPersistFile *shell_link_file; - // save the shortcut - hres = shell_link->QueryInterface(IID_IPersistFile, reinterpret_cast(&shell_link_file)); - if (SUCCEEDED(hres)) - { - hres = shell_link_file->Save(output_path.wc_str(), TRUE); - shell_link_file->Release(); - } - shell_link->Release(); - } -#endif + std::ofstream outputStream(output_path.utf8_string()); + if (!outputStream.good()) + { + auto errorMsg = formatWxString(_("Failed to save desktop entry to {}"), output_path.utf8_string()); + wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); + return; + } + outputStream << desktopEntryString; } -#endif +#elif BOOST_OS_WINDOWS +void wxGameList::CreateShortcut(GameInfo2& gameInfo) +{ + const auto titleId = gameInfo.GetBaseTitleId(); + const auto titleName = wxString::FromUTF8(gameInfo.GetTitleName()); + auto exePath = ActiveSettings::GetExecutablePath(); + + // Get '%APPDATA%\Microsoft\Windows\Start Menu\Programs' path + PWSTR userShortcutFolder; + SHGetKnownFolderPath(FOLDERID_Programs, 0, NULL, &userShortcutFolder); + const wxString shortcutName = wxString::Format("%s.lnk", titleName); + wxFileDialog shortcutDialog(this, _("Choose shortcut location"), _pathToUtf8(userShortcutFolder), shortcutName, + "Shortcut (*.lnk)|*.lnk", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT); + + const auto result = shortcutDialog.ShowModal(); + if (result == wxID_CANCEL) + return; + const auto outputPath = shortcutDialog.GetPath(); + + std::optional icon_path = std::nullopt; + [&]() + { + int iconIdx; + int smallIconIdx; + if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx)) + { + cemuLog_log(LogType::Force, "Icon hasn't loaded"); + return; + } + const auto icon = m_image_list->GetIcon(iconIdx); + PWSTR localAppData; + const auto hres = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData); + wxBitmap bitmap{}; + auto folder = fs::path(localAppData) / "Cemu" / "icons"; + if (!SUCCEEDED(hres) || (!fs::exists(folder) && !fs::create_directories(folder))) + { + cemuLog_log(LogType::Force, "Failed to create icon directory"); + return; + } + if (!bitmap.CopyFromIcon(icon)) + { + cemuLog_log(LogType::Force, "Failed to copy icon"); + return; + } + + icon_path = folder / fmt::format("{:016x}.ico", titleId); + auto stream = wxFileOutputStream(_pathToUtf8(*icon_path)); + auto image = bitmap.ConvertToImage(); + wxICOHandler icohandler{}; + if (!icohandler.SaveFile(&image, stream, false)) + { + icon_path = std::nullopt; + cemuLog_log(LogType::Force, "Icon failed to save"); + } + }(); + + IShellLinkW* shellLink; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast(&shellLink)); + if (SUCCEEDED(hres)) + { + const auto description = wxString::Format("Play %s on Cemu", titleName); + const auto args = wxString::Format("-t %016llx", titleId); + + shellLink->SetPath(exePath.wstring().c_str()); + shellLink->SetDescription(description.wc_str()); + shellLink->SetArguments(args.wc_str()); + shellLink->SetWorkingDirectory(exePath.parent_path().wstring().c_str()); + + if (icon_path) + shellLink->SetIconLocation(icon_path->wstring().c_str(), 0); + else + shellLink->SetIconLocation(exePath.wstring().c_str(), 0); + + IPersistFile* shellLinkFile; + // save the shortcut + hres = shellLink->QueryInterface(IID_IPersistFile, reinterpret_cast(&shellLinkFile)); + if (SUCCEEDED(hres)) + { + hres = shellLinkFile->Save(outputPath.wc_str(), TRUE); + shellLinkFile->Release(); + } + shellLink->Release(); + } + if (!SUCCEEDED(hres)) { + auto errorMsg = formatWxString(_("Failed to save shortcut to {}"), outputPath); + wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); + } +} +#endif \ No newline at end of file From 4d148b369660db629c225bf1ff0e354d025e7902 Mon Sep 17 00:00:00 2001 From: Francesco Saltori Date: Mon, 25 Mar 2024 21:34:40 +0100 Subject: [PATCH 05/10] Add supported locales to macOS plist (#1133) --- src/resource/MacOSXBundleInfo.plist.in | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/resource/MacOSXBundleInfo.plist.in b/src/resource/MacOSXBundleInfo.plist.in index 98064735..ccd1c922 100644 --- a/src/resource/MacOSXBundleInfo.plist.in +++ b/src/resource/MacOSXBundleInfo.plist.in @@ -30,6 +30,28 @@ ${MACOSX_BUNDLE_CATEGORY} LSMinimumSystemVersion ${MACOSX_MINIMUM_SYSTEM_VERSION} + CFBundleLocalizations + + ca + de + en + es + fr + he + hu + it + ja + ko + nb + nl + pl + pt + ru + sv + tr + uk + zh + CFBundleDocumentTypes From 4b7d2f88ae044a590dfa7faeadee1e03047492f0 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:02:37 +0100 Subject: [PATCH 06/10] Latte: Enable colorbuffer optimization if gfx packs are aware The optimization for colorbuffer resolution introduced in PR #706 is now enabled. This optimization changes the resolution of certain framebuffer textures, which may conflict with the texture resolution rules set by some graphic packs. As a result, if a graphic pack that specifies texture resolution rules is in use, the optimization will automatically be turned off to prevent any issues. To circumvent this, graphic packs can now include the setting "colorbufferOptimizationAware = true" in their rules.txt. This setting indicates that the pack has been updated to handle the resolution changes introduced by the optimization. Cemu will allow the optimization to remain enabled if resolution packs have this flag set. --- src/Cafe/GraphicPack/GraphicPack2.cpp | 4 ++++ src/Cafe/GraphicPack/GraphicPack2.h | 3 +++ src/Cafe/HW/Latte/Core/Latte.h | 2 ++ src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp | 21 ++++++++++---------- src/Cafe/HW/Latte/Core/LatteTexture.cpp | 7 ++++--- src/Cafe/HW/Latte/Core/LatteThread.cpp | 17 ++++++++++++++++ 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp index b581316e..27d423b9 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2.cpp @@ -280,6 +280,10 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules) m_enabled = m_default_enabled; } + auto option_allowRendertargetSizeOptimization = rules.FindOption("colorbufferOptimizationAware"); + if (option_allowRendertargetSizeOptimization) + m_allowRendertargetSizeOptimization = boost::iequals(*option_allowRendertargetSizeOptimization, "true") || boost::iequals(*option_allowRendertargetSizeOptimization, "1"); + auto option_vendorFilter = rules.FindOption("vendorFilter"); if (option_vendorFilter) { diff --git a/src/Cafe/GraphicPack/GraphicPack2.h b/src/Cafe/GraphicPack/GraphicPack2.h index 6b07cce9..9b6a86d4 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.h +++ b/src/Cafe/GraphicPack/GraphicPack2.h @@ -113,6 +113,7 @@ public: const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy const std::string& GetDescription() const { return m_description; } bool IsDefaultEnabled() const { return m_default_enabled; } + bool AllowRendertargetSizeOptimization() const { return m_allowRendertargetSizeOptimization; } void SetEnabled(bool state) { m_enabled = state; } @@ -217,6 +218,8 @@ private: bool m_default_enabled = false; + bool m_allowRendertargetSizeOptimization = false; // gfx pack supports framebuffers with non-padded sizes, which is an optional optimization introduced with Cemu 2.0-74 + // filter std::optional m_renderer_api; std::optional m_gfx_vendor; diff --git a/src/Cafe/HW/Latte/Core/Latte.h b/src/Cafe/HW/Latte/Core/Latte.h index d9419a6a..e8cb2be4 100644 --- a/src/Cafe/HW/Latte/Core/Latte.h +++ b/src/Cafe/HW/Latte/Core/Latte.h @@ -25,6 +25,8 @@ struct LatteGPUState_t // context control uint32 contextControl0; uint32 contextControl1; + // optional features + bool allowFramebufferSizeOptimization{false}; // allow using scissor box as size hint to determine non-padded rendertarget size // draw context struct { diff --git a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp index 30069712..f165e257 100644 --- a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp +++ b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp @@ -267,14 +267,15 @@ LatteTextureView* LatteMRT::GetColorAttachmentTexture(uint32 index, bool createN // colorbuffer width/height has to be padded to 8/32 alignment but the actual resolution might be smaller // use the scissor box as a clue to figure out the original resolution if possible -#if 0 - uint32 scissorBoxWidth = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_X(); - uint32 scissorBoxHeight = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_Y(); - if (((scissorBoxWidth + 7) & ~7) == colorBufferWidth) - colorBufferWidth = scissorBoxWidth; - if (((colorBufferHeight + 31) & ~31) == colorBufferHeight) - colorBufferHeight = scissorBoxHeight; -#endif + if(LatteGPUState.allowFramebufferSizeOptimization) + { + uint32 scissorBoxWidth = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_X(); + uint32 scissorBoxHeight = LatteGPUState.contextNew.PA_SC_GENERIC_SCISSOR_BR.get_BR_Y(); + if (((scissorBoxWidth + 7) & ~7) == colorBufferWidth) + colorBufferWidth = scissorBoxWidth; + if (((colorBufferHeight + 31) & ~31) == colorBufferHeight) + colorBufferHeight = scissorBoxHeight; + } // log resolution changes if the above heuristic takes effect // this is useful to find resolutions which need to be updated in gfx pack texture rules @@ -303,7 +304,7 @@ LatteTextureView* LatteMRT::GetColorAttachmentTexture(uint32 index, bool createN if (colorBufferView == nullptr) { // create color buffer view - colorBufferView = LatteTexture_CreateMapping(colorBufferPhysMem, 0, colorBufferWidth, colorBufferHeight, (viewFirstSlice + viewNumSlices), colorBufferPitch, colorBufferTileMode, colorBufferSwizzle>>8, viewFirstMip, 1, viewFirstSlice, viewNumSlices, (Latte::E_GX2SURFFMT)colorBufferFormat, (viewFirstSlice + viewNumSlices)>1? Latte::E_DIM::DIM_2D_ARRAY: Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, false); + colorBufferView = LatteTexture_CreateMapping(colorBufferPhysMem, 0, colorBufferWidth, colorBufferHeight, (viewFirstSlice + viewNumSlices), colorBufferPitch, colorBufferTileMode, colorBufferSwizzle>>8, viewFirstMip, 1, viewFirstSlice, viewNumSlices, (Latte::E_GX2SURFFMT)colorBufferFormat, (viewFirstSlice + viewNumSlices)>1? Latte::E_DIM::DIM_2D_ARRAY: Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, false, true); LatteGPUState.repeatTextureInitialization = true; checkForTextureChanges = false; } @@ -582,7 +583,7 @@ bool LatteMRT::UpdateCurrentFBO() if (!depthBufferView) { // create new depth buffer view and if it doesn't exist then also create the texture - depthBufferView = LatteTexture_CreateMapping(depthBufferPhysMem, 0, depthBufferWidth, depthBufferHeight, depthBufferViewFirstSlice+1, depthBufferPitch, depthBufferTileMode, depthBufferSwizzle, 0, 1, depthBufferViewFirstSlice, 1, depthBufferFormat, depthBufferViewFirstSlice > 0 ? Latte::E_DIM::DIM_2D_ARRAY : Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, true); + depthBufferView = LatteTexture_CreateMapping(depthBufferPhysMem, 0, depthBufferWidth, depthBufferHeight, depthBufferViewFirstSlice+1, depthBufferPitch, depthBufferTileMode, depthBufferSwizzle, 0, 1, depthBufferViewFirstSlice, 1, depthBufferFormat, depthBufferViewFirstSlice > 0 ? Latte::E_DIM::DIM_2D_ARRAY : Latte::E_DIM::DIM_2D, Latte::E_DIM::DIM_2D, true, true); LatteGPUState.repeatTextureInitialization = true; } else diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.cpp b/src/Cafe/HW/Latte/Core/LatteTexture.cpp index d6f576d4..3754fb19 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp @@ -1,5 +1,4 @@ #include "Cafe/HW/Latte/Core/Latte.h" -#include "Cafe/HW/Latte/Core/LatteDraw.h" #include "Cafe/HW/Latte/Core/LatteShader.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" #include "Cafe/HW/Latte/Core/LatteTexture.h" @@ -9,6 +8,8 @@ #include "Cafe/GraphicPack/GraphicPack2.h" +#include + struct TexMemOccupancyEntry { uint32 addrStart; @@ -963,7 +964,7 @@ void LatteTexture_RecreateTextureWithDifferentMipSliceCount(LatteTexture* textur } // create new texture representation -// if allowCreateNewDataTexture is true, a new texture will be created if necessary. If it is false, only existing textures may be used, except if a data-compatible version of the requested texture already exists and it's not view compatible +// if allowCreateNewDataTexture is true, a new texture will be created if necessary. If it is false, only existing textures may be used, except if a data-compatible version of the requested texture already exists and it's not view compatible (todo - we should differentiate between Latte compatible views and renderer compatible) // the returned view will map to the provided mip and slice range within the created texture, this is to match the behavior of lookupSliceEx LatteTextureView* LatteTexture_CreateMapping(MPTR physAddr, MPTR physMipAddr, sint32 width, sint32 height, sint32 depth, sint32 pitch, Latte::E_HWTILEMODE tileMode, uint32 swizzle, sint32 firstMip, sint32 numMip, sint32 firstSlice, sint32 numSlice, Latte::E_GX2SURFFMT format, Latte::E_DIM dimBase, Latte::E_DIM dimView, bool isDepth, bool allowCreateNewDataTexture) { @@ -980,7 +981,7 @@ LatteTextureView* LatteTexture_CreateMapping(MPTR physAddr, MPTR physMipAddr, si // todo, depth and numSlice are redundant sint32 sliceCount = firstSlice + numSlice; - std::vector list_overlappingTextures; + boost::container::small_vector list_overlappingTextures; for (sint32 sliceIndex = 0; sliceIndex < sliceCount; sliceIndex++) { sint32 mipIndex = 0; diff --git a/src/Cafe/HW/Latte/Core/LatteThread.cpp b/src/Cafe/HW/Latte/Core/LatteThread.cpp index bd312d93..a23bd5be 100644 --- a/src/Cafe/HW/Latte/Core/LatteThread.cpp +++ b/src/Cafe/HW/Latte/Core/LatteThread.cpp @@ -175,6 +175,23 @@ int Latte_ThreadEntry() // before doing anything with game specific shaders, we need to wait for graphic packs to finish loading GraphicPack2::WaitUntilReady(); + // if legacy packs are enabled we cannot use the colorbuffer resolution optimization + LatteGPUState.allowFramebufferSizeOptimization = true; + for(auto& pack : GraphicPack2::GetActiveGraphicPacks()) + { + if(pack->AllowRendertargetSizeOptimization()) + continue; + for(auto& rule : pack->GetTextureRules()) + { + if(rule.filter_settings.width >= 0 || rule.filter_settings.height >= 0 || rule.filter_settings.depth >= 0 || + rule.overwrite_settings.width >= 0 || rule.overwrite_settings.height >= 0 || rule.overwrite_settings.depth >= 0) + { + LatteGPUState.allowFramebufferSizeOptimization = false; + cemuLog_log(LogType::Force, "Graphic pack {} prevents rendertarget size optimization.", pack->GetName()); + break; + } + } + } // load disk shader cache LatteShaderCache_Load(); // init registers From fa4ad9b8c196c0821888c0d882154d10feee673b Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:30:39 +0200 Subject: [PATCH 07/10] Gamelist: Add option to hide the icon column (#604) --- src/config/CemuConfig.cpp | 3 +++ src/config/CemuConfig.h | 2 ++ src/gui/components/wxGameList.cpp | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 1801759a..e4be97a7 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -84,6 +84,8 @@ void CemuConfig::Load(XMLConfigParser& parser) game_list_style = gamelist.get("style", 0); game_list_column_order = gamelist.get("order", ""); + show_icon_column = parser.get("show_icon_column", true); + // return default width if value in config file out of range auto loadColumnSize = [&gamelist] (const char *name, uint32 defaultWidth) { @@ -385,6 +387,7 @@ void CemuConfig::Save(XMLConfigParser& parser) psize.set("x", pad_size.x); psize.set("y", pad_size.y); config.set("pad_maximized", pad_maximized); + config.set("show_icon_column" , show_icon_column); auto gamelist = config.set("GameList"); gamelist.set("style", game_list_style); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index eb552fce..bcaf8467 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -418,6 +418,8 @@ struct CemuConfig ConfigValue did_show_graphic_pack_download{false}; ConfigValue did_show_macos_disclaimer{false}; + ConfigValue show_icon_column{ false }; + int game_list_style = 0; std::string game_list_column_order; struct diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index 73bcd98d..d7c9a4f8 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -88,7 +88,10 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id) const auto& config = GetConfig(); InsertColumn(ColumnHiddenName, "", wxLIST_FORMAT_LEFT, 0); - InsertColumn(ColumnIcon, "", wxLIST_FORMAT_LEFT, kListIconWidth); + if(config.show_icon_column) + InsertColumn(ColumnIcon, _("Icon"), wxLIST_FORMAT_LEFT, kListIconWidth); + else + InsertColumn(ColumnIcon, _("Icon"), wxLIST_FORMAT_LEFT, 0); InsertColumn(ColumnName, _("Game"), wxLIST_FORMAT_LEFT, config.column_width.name); InsertColumn(ColumnVersion, _("Version"), wxLIST_FORMAT_RIGHT, config.column_width.version); InsertColumn(ColumnDLC, _("DLC"), wxLIST_FORMAT_RIGHT, config.column_width.dlc); @@ -794,6 +797,7 @@ void wxGameList::OnColumnRightClick(wxListEvent& event) ResetWidth = wxID_HIGHEST + 1, ResetOrder, + ShowIcon, ShowName, ShowVersion, ShowDlc, @@ -810,6 +814,7 @@ void wxGameList::OnColumnRightClick(wxListEvent& event) menu.Append(ResetOrder, _("Reset &order")) ; menu.AppendSeparator(); + menu.AppendCheckItem(ShowIcon, _("Show &icon"))->Check(GetColumnWidth(ColumnIcon) > 0); menu.AppendCheckItem(ShowName, _("Show &name"))->Check(GetColumnWidth(ColumnName) > 0); menu.AppendCheckItem(ShowVersion, _("Show &version"))->Check(GetColumnWidth(ColumnVersion) > 0); menu.AppendCheckItem(ShowDlc, _("Show &dlc"))->Check(GetColumnWidth(ColumnDLC) > 0); @@ -828,6 +833,9 @@ void wxGameList::OnColumnRightClick(wxListEvent& event) switch (event.GetId()) { + case ShowIcon: + config.show_icon_column = menu->IsChecked(ShowIcon); + break; case ShowName: config.column_width.name = menu->IsChecked(ShowName) ? DefaultColumnSize::name : 0; break; @@ -907,7 +915,10 @@ void wxGameList::ApplyGameListColumnWidths() { const auto& config = GetConfig(); wxWindowUpdateLocker lock(this); - SetColumnWidth(ColumnIcon, kListIconWidth); + if(config.show_icon_column) + SetColumnWidth(ColumnIcon, kListIconWidth); + else + SetColumnWidth(ColumnIcon, 0); SetColumnWidth(ColumnName, config.column_width.name); SetColumnWidth(ColumnVersion, config.column_width.version); SetColumnWidth(ColumnDLC, config.column_width.dlc); From 111e383d1b0c2a27001433781e2cba123f991048 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:07:08 +0100 Subject: [PATCH 08/10] coreinit: Fix race condition that causes crash (#1138) --- src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 8ce5de07..809d7be4 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -1159,9 +1159,11 @@ namespace coreinit #include std::vector g_schedulerThreadIds; + std::mutex g_schedulerThreadIdsLock; std::vector& OSGetSchedulerThreadIds() { + std::lock_guard schedulerThreadIdsLockGuard(g_schedulerThreadIdsLock); return g_schedulerThreadIds; } #endif @@ -1183,7 +1185,10 @@ namespace coreinit } pid_t tid = gettid(); - g_schedulerThreadIds.emplace_back(tid); + { + std::lock_guard schedulerThreadIdsLockGuard(g_schedulerThreadIdsLock); + g_schedulerThreadIds.emplace_back(tid); + } #endif t_schedulerFiber = Fiber::PrepareCurrentThread(); @@ -1238,7 +1243,10 @@ namespace coreinit sSchedulerThreads.clear(); g_schedulerThreadHandles.clear(); #if BOOST_OS_LINUX - g_schedulerThreadIds.clear(); + { + std::lock_guard schedulerThreadIdsLockGuard(g_schedulerThreadIdsLock); + g_schedulerThreadIds.clear(); + } #endif // clean up all fibers for (auto& it : g_idleLoopFiber) From 4f3d4624f5c73d5f2634b09142f4fb7ea7fba623 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:09:24 +0100 Subject: [PATCH 09/10] GraphicPacksWindow: Disable update button when a game is running (#1137) --- src/gui/DownloadGraphicPacksWindow.cpp | 16 ++++++---------- src/gui/GraphicPacksWindow2.cpp | 10 ++++++++++ src/gui/GraphicPacksWindow2.h | 1 + src/gui/MainWindow.cpp | 10 ++++++++++ src/gui/MainWindow.h | 4 +++- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/gui/DownloadGraphicPacksWindow.cpp b/src/gui/DownloadGraphicPacksWindow.cpp index 8189b50a..03f102d2 100644 --- a/src/gui/DownloadGraphicPacksWindow.cpp +++ b/src/gui/DownloadGraphicPacksWindow.cpp @@ -110,14 +110,6 @@ void deleteDownloadedGraphicPacks() void DownloadGraphicPacksWindow::UpdateThread() { - if (CafeSystem::IsTitleRunning()) - { - wxMessageBox(_("Graphic packs cannot be updated while a game is running."), _("Graphic packs"), 5, this->GetParent()); - // cancel update - m_threadState = ThreadFinished; - return; - } - // get github url std::string githubAPIUrl; curlDownloadFileState_t tempDownloadState; @@ -326,8 +318,6 @@ DownloadGraphicPacksWindow::DownloadGraphicPacksWindow(wxWindow* parent) m_downloadState = std::make_unique(); - - m_thread = std::thread(&DownloadGraphicPacksWindow::UpdateThread, this); } DownloadGraphicPacksWindow::~DownloadGraphicPacksWindow() @@ -344,6 +334,12 @@ const std::string& DownloadGraphicPacksWindow::GetException() const int DownloadGraphicPacksWindow::ShowModal() { + if(CafeSystem::IsTitleRunning()) + { + wxMessageBox(_("Graphic packs cannot be updated while a game is running."), _("Graphic packs"), 5, this->GetParent()); + return wxID_CANCEL; + } + m_thread = std::thread(&DownloadGraphicPacksWindow::UpdateThread, this); wxDialog::ShowModal(); return m_threadState == ThreadCanceled ? wxID_CANCEL : wxID_OK; } diff --git a/src/gui/GraphicPacksWindow2.cpp b/src/gui/GraphicPacksWindow2.cpp index 78b344d5..29f4b865 100644 --- a/src/gui/GraphicPacksWindow2.cpp +++ b/src/gui/GraphicPacksWindow2.cpp @@ -319,6 +319,7 @@ GraphicPacksWindow2::GraphicPacksWindow2(wxWindow* parent, uint64_t title_id_fil SetSizer(main_sizer); + UpdateTitleRunning(CafeSystem::IsTitleRunning()); FillGraphicPackList(); } @@ -676,6 +677,15 @@ void GraphicPacksWindow2::OnInstalledGamesChanged(wxCommandEvent& event) event.Skip(); } +void GraphicPacksWindow2::UpdateTitleRunning(bool running) +{ + m_update_graphicPacks->Enable(!running); + if(running) + m_update_graphicPacks->SetToolTip(_("Graphic packs cannot be updated while a game is running.")); + else + m_update_graphicPacks->SetToolTip(nullptr); +} + void GraphicPacksWindow2::ReloadPack(const GraphicPackPtr& graphic_pack) const { if (graphic_pack->HasShaders() || graphic_pack->HasPatches() || graphic_pack->HasCustomVSyncFrequency()) diff --git a/src/gui/GraphicPacksWindow2.h b/src/gui/GraphicPacksWindow2.h index a068f2b6..a79c62c1 100644 --- a/src/gui/GraphicPacksWindow2.h +++ b/src/gui/GraphicPacksWindow2.h @@ -21,6 +21,7 @@ public: ~GraphicPacksWindow2(); static void RefreshGraphicPacks(); + void UpdateTitleRunning(bool running); private: std::string m_filter; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 311ddfb7..023918bd 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -625,6 +625,7 @@ bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATE CreateCanvas(); CafeSystem::LaunchForegroundTitle(); RecreateMenu(); + UpdateChildWindowTitleRunningState(); return true; } @@ -683,6 +684,7 @@ void MainWindow::OnFileMenu(wxCommandEvent& event) RecreateMenu(); CreateGameListAndStatusBar(); DoLayout(); + UpdateChildWindowTitleRunningState(); } } @@ -2320,6 +2322,14 @@ void MainWindow::RecreateMenu() SetMenuVisible(false); } +void MainWindow::UpdateChildWindowTitleRunningState() +{ + const bool running = CafeSystem::IsTitleRunning(); + + if(m_graphic_pack_window) + m_graphic_pack_window->UpdateTitleRunning(running); +} + void MainWindow::RestoreSettingsAfterGameExited() { RecreateMenu(); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 88d2a1d3..7191df12 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -21,6 +21,7 @@ class DebuggerWindow2; struct GameEntry; class DiscordPresence; class TitleManager; +class GraphicPacksWindow2; class wxLaunchGameEvent; wxDECLARE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent); @@ -146,6 +147,7 @@ public: private: void RecreateMenu(); + void UpdateChildWindowTitleRunningState(); static wxString GetInitialWindowTitle(); void ShowGettingStartedDialog(); @@ -163,7 +165,7 @@ private: MemorySearcherTool* m_toolWindow = nullptr; TitleManager* m_title_manager = nullptr; PadViewFrame* m_padView = nullptr; - wxWindow* m_graphic_pack_window = nullptr; + GraphicPacksWindow2* m_graphic_pack_window = nullptr; wxTimer* m_timer; wxPoint m_mouse_position{}; From 5230fcab374b6180043b0c1397c9d51e0c99b84a Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:14:01 +0100 Subject: [PATCH 10/10] Debugger: Fix infinite loop in symbol storage (#1134) --- src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h b/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h index aba6a9b5..0a46951d 100644 --- a/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h +++ b/src/Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h @@ -45,12 +45,17 @@ public: static void ClearRange(MPTR address, uint32 length) { + if (length == 0) + return; s_lock.lock(); - while (length > 0) + for (;;) { auto itr = s_typeStorage.find(address); if (itr != s_typeStorage.end()) s_typeStorage.erase(itr); + + if (length <= 4) + break; address += 4; length -= 4; } @@ -60,4 +65,4 @@ public: private: static FSpinlock s_lock; static std::unordered_map s_typeStorage; -}; \ No newline at end of file +};