diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index db78ce3d91..6852c11b00 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -2,8 +2,7 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
-#include <atomic>
-#include <chrono>
+#include <thread>
 
 #include "common/settings.h"
 #include "video_core/renderer_vulkan/vk_master_semaphore.h"
@@ -12,8 +11,6 @@
 
 namespace Vulkan {
 
-using namespace std::chrono_literals;
-
 MasterSemaphore::MasterSemaphore(const Device& device) {
     static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{
         .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR,
@@ -34,9 +31,9 @@ MasterSemaphore::MasterSemaphore(const Device& device) {
     // Validation layers have a bug where they fail to track resource usage when using timeline
     // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have
     // a separate thread waiting for each timeline semaphore value.
-    debug_thread = std::thread([this] {
+    debug_thread = std::jthread([this](std::stop_token stop_token) {
         u64 counter = 0;
-        while (!shutdown) {
+        while (!stop_token.stop_requested()) {
             if (semaphore.Wait(counter, 10'000'000)) {
                 ++counter;
             }
@@ -44,13 +41,6 @@ MasterSemaphore::MasterSemaphore(const Device& device) {
     });
 }
 
-MasterSemaphore::~MasterSemaphore() {
-    shutdown = true;
-
-    // This thread might not be started
-    if (debug_thread.joinable()) {
-        debug_thread.join();
-    }
-}
+MasterSemaphore::~MasterSemaphore() = default;
 
 } // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 4b6d64daa3..ee3cd35d09 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -65,11 +65,10 @@ public:
     }
 
 private:
-    vk::Semaphore semaphore;           ///< Timeline semaphore.
-    std::atomic<u64> gpu_tick{0};      ///< Current known GPU tick.
-    std::atomic<u64> current_tick{1};  ///< Current logical tick.
-    std::atomic<bool> shutdown{false}; ///< True when the object is being destroyed.
-    std::thread debug_thread;          ///< Debug thread to workaround validation layer bugs.
+    vk::Semaphore semaphore;          ///< Timeline semaphore.
+    std::atomic<u64> gpu_tick{0};     ///< Current known GPU tick.
+    std::atomic<u64> current_tick{1}; ///< Current logical tick.
+    std::jthread debug_thread;        ///< Debug thread to workaround validation layer bugs.
 };
 
 } // namespace Vulkan