From 01195e9bfe221f6906254062d4e7ef2c474ff379 Mon Sep 17 00:00:00 2001 From: FakeMichau <49685661+FakeMichau@users.noreply.github.com> Date: Tue, 13 Oct 2026 01:29:24 +0200 Subject: [PATCH] Fix imgui causing a crash when using DLSSG on Vulkan Slopped. --- OptiScaler/State.h | 1 - OptiScaler/hooks/Streamline_Hooks.cpp | 16 -- .../include/imgui/imgui_impl_vulkan.cpp | 6 + OptiScaler/include/imgui/imgui_impl_vulkan.h | 1 + OptiScaler/menu/menu_common.cpp | 6 - OptiScaler/menu/menu_overlay_vk.cpp | 140 +++++++++--------- 6 files changed, 73 insertions(+), 97 deletions(-) diff --git a/OptiScaler/State.h b/OptiScaler/State.h index 530d06fa..1faf0b45 100644 --- a/OptiScaler/State.h +++ b/OptiScaler/State.h @@ -194,7 +194,6 @@ class State bool dlssgDebugView = false; bool dlssgInterpolatedOnly = false; uint64_t dlssgLastFrame = 0; - uint32_t delayMenuRenderBy = 0; // FSR Common float lastFsrCameraNear = 0.0f; diff --git a/OptiScaler/hooks/Streamline_Hooks.cpp b/OptiScaler/hooks/Streamline_Hooks.cpp index 43af14a3..8f59fea2 100644 --- a/OptiScaler/hooks/Streamline_Hooks.cpp +++ b/OptiScaler/hooks/Streamline_Hooks.cpp @@ -7,8 +7,6 @@ #include #include -#include -#include #include #include #include @@ -1349,20 +1347,6 @@ sl::Result StreamlineHooks::hkslDLSSGSetOptions(const sl::ViewportHandle& viewpo newOptions.dynamicTargetFrameRate = Config::Instance()->FGDLSSGFramerateTargetDMFG.value(); } - if (state.swapchainApi == API::Vulkan) - { - // Only matters for Vulkan, DX doesn't use this delay - if (dlssgPotentiallyActive && !MenuOverlayBase::IsVisible()) - state.delayMenuRenderBy = 10; - - if (MenuOverlayBase::IsVisible()) - { - newOptions.mode = sl::DLSSGMode::eOff; - newOptions.flags |= sl::DLSSGFlags::eRetainResourcesWhenOff; - ReflexHooks::setDlssgFrameCount(0); - } - } - LOG_TRACE("DLSSG Modified Mode: {}", magic_enum::enum_name(newOptions.mode)); if (dlssgPotentiallyActive && state.streamlineVersion >= feature_version { 2, 7, 1 }) diff --git a/OptiScaler/include/imgui/imgui_impl_vulkan.cpp b/OptiScaler/include/imgui/imgui_impl_vulkan.cpp index bc8da644..2929f6c8 100644 --- a/OptiScaler/include/imgui/imgui_impl_vulkan.cpp +++ b/OptiScaler/include/imgui/imgui_impl_vulkan.cpp @@ -1328,6 +1328,12 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) bd->VulkanInitInfo.MinImageCount = min_image_count; } +void ImGui_ImplVulkan_SetQueue(VkQueue queue) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + bd->VulkanInitInfo.Queue = queue; +} + // Register a texture by creating a descriptor // FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) diff --git a/OptiScaler/include/imgui/imgui_impl_vulkan.h b/OptiScaler/include/imgui/imgui_impl_vulkan.h index 543f767b..a61165ee 100644 --- a/OptiScaler/include/imgui/imgui_impl_vulkan.h +++ b/OptiScaler/include/imgui/imgui_impl_vulkan.h @@ -114,6 +114,7 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(bool shutdown_platform IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) +IMGUI_IMPL_API void ImGui_ImplVulkan_SetQueue(VkQueue queue); // Queue used for texture uploads // (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex); diff --git a/OptiScaler/menu/menu_common.cpp b/OptiScaler/menu/menu_common.cpp index a11a92fa..d5e67cb3 100644 --- a/OptiScaler/menu/menu_common.cpp +++ b/OptiScaler/menu/menu_common.cpp @@ -5027,12 +5027,6 @@ void MenuCommon::RenderFrameGenerationRuntimeSettings(RenderMenuContext& ctx) "GPUs under Windows\nCan cause stutters, so best to use only when necessary"); } } - else if (state.swapchainApi == Vulkan) - { - ImGui::TextColored(toneMapColor(ImVec4(1.f, 0.8f, 0.f, 1.f)), - "DLSSG is purposefully disabled when this menu is visible"); - ImGui::Spacing(); - } } bool isLoaded = false; diff --git a/OptiScaler/menu/menu_overlay_vk.cpp b/OptiScaler/menu/menu_overlay_vk.cpp index 6d49566d..72e31d6c 100644 --- a/OptiScaler/menu/menu_overlay_vk.cpp +++ b/OptiScaler/menu/menu_overlay_vk.cpp @@ -554,11 +554,11 @@ bool MenuOverlayVk::QueuePresent(VkQueue queue, VkPresentInfoKHR* pPresentInfo) { auto semaphoreIndex = _frameCount % _scImageCount; + // With DLSSG the present comes from Streamline's thread, while the game's queue + // can be waiting on that thread. Using the presenting queue avoids a deadlock. + ImGui_ImplVulkan_SetQueue(queue); ImGui_ImplVulkan_NewFrame(); - if (State::Instance().delayMenuRenderBy > 0) - State::Instance().delayMenuRenderBy--; - // Blur images are only created once the menu is opened, menu decides if they are used bool blurReady = false; if (_menuBlur || MenuOverlayBase::IsVisible()) @@ -576,81 +576,73 @@ bool MenuOverlayVk::QueuePresent(VkQueue queue, VkPresentInfoKHR* pPresentInfo) if (MenuOverlayBase::RenderMenu()) { - if (State::Instance().delayMenuRenderBy == 0) + uint32_t idx = pPresentInfo->pImageIndices[0]; + ImGui_ImplVulkanH_Frame* fd = &_ImVulkan_Frames[idx]; + const bool blurMenu = blurReady && MenuCommon::BackgroundBlurUsed(); + + vkWaitForFences(_ImVulkan_Info.Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); + vkResetFences(_ImVulkan_Info.Device, 1, &fd->Fence); + { - uint32_t idx = pPresentInfo->pImageIndices[0]; - ImGui_ImplVulkanH_Frame* fd = &_ImVulkan_Frames[idx]; - const bool blurMenu = blurReady && MenuCommon::BackgroundBlurUsed(); - - vkWaitForFences(_ImVulkan_Info.Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); - vkResetFences(_ImVulkan_Info.Device, 1, &fd->Fence); - - { - vkResetCommandPool(_ImVulkan_Info.Device, fd->CommandPool, 0); - VkCommandBufferBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - vkBeginCommandBuffer(fd->CommandBuffer, &info); - } - - // Needs to be outside of the render pass - if (blurMenu) - _menuBlur->Dispatch(fd->CommandBuffer, fd->Backbuffer); - - { - VkRenderPassBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - info.renderPass = _vkRenderPass; - info.framebuffer = fd->Framebuffer; - info.renderArea.extent.width = static_cast(ImGui::GetIO().DisplaySize.x); - info.renderArea.extent.height = static_cast(ImGui::GetIO().DisplaySize.y); - vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); - } - - ImGui::Render(); - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), fd->CommandBuffer); - - // Submit command buffer - vkCmdEndRenderPass(fd->CommandBuffer); - auto ecbResult = vkEndCommandBuffer(fd->CommandBuffer); - if (ecbResult != VK_SUCCESS) - { - LOG_ERROR("vkQueueSubmit error: {0:X}", (UINT) ecbResult); - return false; - } - - // Submit queue and semaphores - LOG_DEBUG("waitSemaphoreCount: {0}", pPresentInfo->waitSemaphoreCount); - // Blur copies the image with a transfer, so everything has to wait for the game - VkPipelineStageFlags waitStages[8]; - std::fill(std::begin(waitStages), std::end(waitStages), - blurMenu ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - - VkSubmitInfo submit_info = {}; - submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &fd->CommandBuffer; - submit_info.pWaitDstStageMask = waitStages; - submit_info.waitSemaphoreCount = pPresentInfo->waitSemaphoreCount; - submit_info.pWaitSemaphores = pPresentInfo->pWaitSemaphores; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &_ImVulkan_Semaphores[semaphoreIndex]; - - auto qResult = vkQueueSubmit(_ImVulkan_Info.Queue, 1, &submit_info, fd->Fence); - if (qResult != VK_SUCCESS) - { - LOG_ERROR("vkQueueSubmit error: {0:X}", (UINT) qResult); - return false; - } - - pPresentInfo->waitSemaphoreCount = 1; - pPresentInfo->pWaitSemaphores = &_ImVulkan_Semaphores[semaphoreIndex]; + vkResetCommandPool(_ImVulkan_Info.Device, fd->CommandPool, 0); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(fd->CommandBuffer, &info); } - else + + // Needs to be outside of the render pass + if (blurMenu) + _menuBlur->Dispatch(fd->CommandBuffer, fd->Backbuffer); + { - // To make RenderMenu happy as it expects this - ImGui::Render(); + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = _vkRenderPass; + info.framebuffer = fd->Framebuffer; + info.renderArea.extent.width = static_cast(ImGui::GetIO().DisplaySize.x); + info.renderArea.extent.height = static_cast(ImGui::GetIO().DisplaySize.y); + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } + + ImGui::Render(); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), fd->CommandBuffer); + + // Submit command buffer + vkCmdEndRenderPass(fd->CommandBuffer); + auto ecbResult = vkEndCommandBuffer(fd->CommandBuffer); + if (ecbResult != VK_SUCCESS) + { + LOG_ERROR("vkQueueSubmit error: {0:X}", (UINT) ecbResult); + return false; + } + + // Submit queue and semaphores + LOG_DEBUG("waitSemaphoreCount: {0}", pPresentInfo->waitSemaphoreCount); + // Blur copies the image with a transfer, so everything has to wait for the game + VkPipelineStageFlags waitStages[8]; + std::fill(std::begin(waitStages), std::end(waitStages), + blurMenu ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &fd->CommandBuffer; + submit_info.pWaitDstStageMask = waitStages; + submit_info.waitSemaphoreCount = pPresentInfo->waitSemaphoreCount; + submit_info.pWaitSemaphores = pPresentInfo->pWaitSemaphores; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &_ImVulkan_Semaphores[semaphoreIndex]; + + auto qResult = vkQueueSubmit(queue, 1, &submit_info, fd->Fence); + if (qResult != VK_SUCCESS) + { + LOG_ERROR("vkQueueSubmit error: {0:X}", (UINT) qResult); + return false; + } + + pPresentInfo->waitSemaphoreCount = 1; + pPresentInfo->pWaitSemaphores = &_ImVulkan_Semaphores[semaphoreIndex]; } }