From 986eee7ef37f07c3e1b8973467c5fcf22303299c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 18 Jul 2026 23:45:17 +0200 Subject: [PATCH] Remove the global variable "useEmuThread", add some VK error checking for compute shader compiles --- GPU/Common/TextureCacheCommon.cpp | 2 +- GPU/Vulkan/TextureCacheVulkan.cpp | 12 ++++++++++-- GPU/Vulkan/VulkanUtil.cpp | 8 ++++++-- GPU/Vulkan/VulkanUtil.h | 2 +- Windows/EmuThread.cpp | 26 +++++++------------------- Windows/EmuThread.h | 2 +- Windows/MainWindow.cpp | 2 +- Windows/main.cpp | 2 +- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index f36fed109a..25ece3e25b 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2792,7 +2792,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt } } - // Check early for the degenerate case from Tactics Ogre. + // Check early for the degenerate case from Tactics Ogre (EN version, japanese is worse!). if (pure3D && plan.levelsToLoad == 2 && gstate.getTextureAddress(0) == gstate.getTextureAddress(1)) { // Simply treat it as a regular 2D texture, no fake mipmaps or anything. // levelsToLoad/Create gets set to 1 on the way out from the surrounding if. diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 14f53a5a23..12b2fd1f78 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -515,8 +515,12 @@ bool TextureCacheVulkan::RunMultipassCompute(VulkanContext *vulkan, VkCommandBuf stage.useFinalOutputSize ? dstWidth : srcWidth * stage.dstWidthScale, stage.useFinalOutputSize ? dstHeight : srcHeight * stage.dstHeightScale, }; + VkPipeline pipeline = computeShaderManager_.GetPipeline(multipassCS_[stage.shaderIndex], "Multipass Compute Shader"); + if (!pipeline) { + return false; + } VkDescriptorSet stageSet = computeShaderManager_.GetDescriptorSet(outputView, inputBuffer, inputOffset, inputRange, VK_NULL_HANDLE, 0, 0, inputImage, textureScaleCBuffer_.Buffer(), textureScaleCBuffer_.Size()); - vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipeline(multipassCS_[stage.shaderIndex])); + vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); vkCmdBindDescriptorSets(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipelineLayout(), 0, 1, &stageSet, 0, nullptr); vkCmdPushConstants(cmdInit, computeShaderManager_.GetPipelineLayout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(params), ¶ms); vkCmdDispatch(cmdInit, (params.dstWidth + 7) / 8, (params.dstHeight + 7) / 8, 1); @@ -542,7 +546,11 @@ bool TextureCacheVulkan::ScaleBufferToImage(VulkanContext *vulkan, VkCommandBuff } VkDescriptorSet descSet = computeShaderManager_.GetDescriptorSet(dstView, texBuf, bufferOffset, srcSize, VK_NULL_HANDLE, 0, 0, VK_NULL_HANDLE, textureScaleCBuffer_.Buffer(), textureScaleCBuffer_.Size()); struct Params { int x; int y; } params{ srcWidth, srcHeight }; - vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipeline(singlePassCS_)); + VkPipeline pipeline = computeShaderManager_.GetPipeline(singlePassCS_, "Single Pass Compute Shader"); + if (!pipeline) { + return false; + } + vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); vkCmdBindDescriptorSets(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipelineLayout(), 0, 1, &descSet, 0, nullptr); vkCmdPushConstants(cmdInit, computeShaderManager_.GetPipelineLayout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(params), ¶ms); vkCmdDispatch(cmdInit, (srcWidth + 7) / 8, (srcHeight + 7) / 8, 1); diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index b3a4659d7e..4ccf56cf46 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -262,7 +262,7 @@ VkDescriptorSet VulkanComputeShaderManager::GetDescriptorSet(VkImageView image, return desc; } -VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs) { +VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs, const char *tag) { PipelineKey key{ cs }; VkPipeline pipeline; if (pipelines_.Get(key, &pipeline)) { @@ -278,7 +278,11 @@ VkPipeline VulkanComputeShaderManager::GetPipeline(VkShaderModule cs) { pci.flags = 0; VkResult res = vkCreateComputePipelines(vulkan_->GetDevice(), pipelineCache_, 1, &pci, nullptr, &pipeline); - _assert_(res == VK_SUCCESS); + if (res != VK_SUCCESS) { + ERROR_LOG(Log::G3D, "Failed to create compute pipeline from shader module (%s)", tag); + pipelines_.Insert(key, VK_NULL_HANDLE); + } + _dbg_assert_msg_(res == VK_SUCCESS, "Failed to create compute pipeline from shader module (%s)", tag); pipelines_.Insert(key, pipeline); return pipeline; diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 6e6d683d29..5f6fa11eee 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -58,7 +58,7 @@ public: VkDescriptorSet GetDescriptorSet(VkImageView image, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, VkBuffer buffer2, VkDeviceSize offset2, VkDeviceSize range2, VkImageView image2, VkBuffer cbuffer, VkDeviceSize cbufferSize); // This of course caches though. - VkPipeline GetPipeline(VkShaderModule cs); + VkPipeline GetPipeline(VkShaderModule cs, const char *tag); VkPipelineLayout GetPipelineLayout() const { return pipelineLayout_; } void ClearPipelines(); diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 5e6fe75e77..b88f8eb75f 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -43,7 +43,6 @@ static std::thread emuThread; static std::atomic g_emuThreadState(EmuThreadState::DISABLED); static std::thread mainThread; -static bool useEmuThread; static std::string g_error_message; static bool g_inLoop; @@ -54,8 +53,7 @@ static GraphicsContext *g_graphicsContext; void MainThreadFunc(); -void MainThread_Start(bool separateEmuThread) { - useEmuThread = separateEmuThread; +void MainThread_Start() { mainThread = std::thread(&MainThreadFunc); } @@ -146,7 +144,8 @@ bool CreateGraphicsBackend(std::string *error_message, GraphicsContext **ctx) { } void MainThreadFunc() { - // We'll start up a separate thread we'll call Emu + const bool useEmuThread = g_Config.iGPUBackend == (int)GPUBackend::OPENGL; + SetCurrentThreadName(useEmuThread ? "RenderThread" : "EmuThread"); System_SetWindowTitle(""); @@ -154,27 +153,17 @@ void MainThreadFunc() { // We convert command line arguments to UTF-8 immediately. std::vector wideArgs = GetWideCmdLine(); std::vector argsUTF8; - for (auto& string : wideArgs) { + for (const auto &string : wideArgs) { argsUTF8.push_back(ConvertWStringToUTF8(string)); } std::vector args; - for (auto& string : argsUTF8) { + for (const auto &string : argsUTF8) { args.push_back(string.c_str()); } - bool performingRestart = NativeIsRestarting(); + + const bool performingRestart = NativeIsRestarting(); NativeInit(static_cast(args.size()), &args[0], "", "", nullptr); - if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { - if (!useEmuThread) { - // Okay, we must've switched to OpenGL. Let's flip the emu thread on. - useEmuThread = true; - SetCurrentThreadName("Render"); - } - } else if (useEmuThread) { - // We must've failed over from OpenGL, flip the emu thread off. - useEmuThread = false; - SetCurrentThreadName("EmuThread"); - } if (g_Config.sFailedGPUBackends.find("ALL") != std::string::npos) { Reporting::ReportMessage("Graphics init error: %s", "ALL"); @@ -305,7 +294,6 @@ void MainThreadFunc() { g_graphicsContext->ThreadEnd(); g_graphicsContext->ShutdownFromRenderThread(); - g_graphicsContext->Shutdown(); delete g_graphicsContext; diff --git a/Windows/EmuThread.h b/Windows/EmuThread.h index bc04bc3f06..f630b50763 100644 --- a/Windows/EmuThread.h +++ b/Windows/EmuThread.h @@ -17,6 +17,6 @@ #pragma once -void MainThread_Start(bool separateRenderThread); +void MainThread_Start(); void MainThread_Stop(); bool MainThread_Ready(); diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 961de47173..ed93c6e13d 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -1102,7 +1102,7 @@ namespace MainWindow { g_InputManager.StopPolling(); MainThread_Stop(); UpdateUIState(UISTATE_MENU); - MainThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL); + MainThread_Start(); g_InputManager.BeginPolling(); break; diff --git a/Windows/main.cpp b/Windows/main.cpp index e0ddc78c50..a123f367bd 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -1230,7 +1230,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin // Emu thread (and render thread, if any) is always running! // Only OpenGL uses an externally managed render thread (due to GL's single-threaded context design). Vulkan // manages its own render thread. - MainThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL); + MainThread_Start(); g_InputManager.BeginPolling();