diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index ddf28ca282..454bb66a47 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -12,6 +12,7 @@
 #include <fmt/format.h>
 
 #include "common/logging/log.h"
+#include "common/polyfill_ranges.h"
 #include "common/scope_exit.h"
 #include "common/settings.h"
 #include "common/telemetry.h"
@@ -65,6 +66,21 @@ std::string BuildCommaSeparatedExtensions(
     return fmt::format("{}", fmt::join(available_extensions, ","));
 }
 
+DebugCallback MakeDebugCallback(const vk::Instance& instance, const vk::InstanceDispatch& dld) {
+    if (!Settings::values.renderer_debug) {
+        return DebugCallback{};
+    }
+    const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
+    const auto it = std::ranges::find_if(*properties, [](const auto& prop) {
+        return std::strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, prop.extensionName) == 0;
+    });
+    if (it != properties->end()) {
+        return CreateDebugUtilsCallback(instance);
+    } else {
+        return CreateDebugReportCallback(instance);
+    }
+}
+
 } // Anonymous namespace
 
 Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
@@ -87,7 +103,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
       cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())),
       instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
                               Settings::values.renderer_debug.GetValue())),
-      debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
+      debug_callback(MakeDebugCallback(instance, dld)),
       surface(CreateSurface(instance, render_window.GetWindowInfo())),
       device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(),
       scheduler(device, state_tracker),
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index b2e8cbd1bb..ca22c0baa3 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -5,6 +5,7 @@
 
 #include <memory>
 #include <string>
+#include <variant>
 
 #include "common/dynamic_library.h"
 #include "video_core/renderer_base.h"
@@ -33,6 +34,8 @@ class GPU;
 
 namespace Vulkan {
 
+using DebugCallback = std::variant<vk::DebugUtilsMessenger, vk::DebugReportCallback>;
+
 Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
                     VkSurfaceKHR surface);
 
@@ -71,7 +74,7 @@ private:
     vk::InstanceDispatch dld;
 
     vk::Instance instance;
-    vk::DebugUtilsMessenger debug_callback;
+    DebugCallback debug_callback;
     vk::SurfaceKHR surface;
 
     ScreenInfo screen_info;
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
index 9de484c294..67e8065a47 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
@@ -7,10 +7,10 @@
 
 namespace Vulkan {
 namespace {
-VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
-                  VkDebugUtilsMessageTypeFlagsEXT type,
-                  const VkDebugUtilsMessengerCallbackDataEXT* data,
-                  [[maybe_unused]] void* user_data) {
+VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
+                           VkDebugUtilsMessageTypeFlagsEXT type,
+                           const VkDebugUtilsMessengerCallbackDataEXT* data,
+                           [[maybe_unused]] void* user_data) {
     // Skip logging known false-positive validation errors
     switch (static_cast<u32>(data->messageIdNumber)) {
 #ifdef ANDROID
@@ -62,9 +62,26 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
     }
     return VK_FALSE;
 }
+
+VkBool32 DebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType,
+                             uint64_t object, size_t location, int32_t messageCode,
+                             const char* pLayerPrefix, const char* pMessage, void* pUserData) {
+    const VkDebugReportFlagBitsEXT severity = static_cast<VkDebugReportFlagBitsEXT>(flags);
+    const std::string_view message{pMessage};
+    if (severity & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
+        LOG_CRITICAL(Render_Vulkan, "{}", message);
+    } else if (severity & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
+        LOG_WARNING(Render_Vulkan, "{}", message);
+    } else if (severity & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
+        LOG_INFO(Render_Vulkan, "{}", message);
+    } else if (severity & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
+        LOG_DEBUG(Render_Vulkan, "{}", message);
+    }
+    return VK_FALSE;
+}
 } // Anonymous namespace
 
-vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) {
+vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) {
     return instance.CreateDebugUtilsMessenger(VkDebugUtilsMessengerCreateInfoEXT{
         .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
         .pNext = nullptr,
@@ -76,7 +93,18 @@ vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) {
         .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                        VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
                        VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
-        .pfnUserCallback = Callback,
+        .pfnUserCallback = DebugUtilCallback,
+        .pUserData = nullptr,
+    });
+}
+
+vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance) {
+    return instance.CreateDebugReportCallback({
+        .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
+        .pNext = nullptr,
+        .flags = VK_DEBUG_REPORT_DEBUG_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
+                 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
+        .pfnCallback = DebugReportCallback,
         .pUserData = nullptr,
     });
 }
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h
index 71b1f69eca..a8af7b406b 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.h
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.h
@@ -7,6 +7,8 @@
 
 namespace Vulkan {
 
-vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance);
+vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance);
+
+vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance);
 
 } // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index e4ca65b583..9743a82f5c 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -349,7 +349,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
     const bool is_s8gen2 = device_id == 0x43050a01;
     const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
 
-    if ((is_mvk || is_qualcomm || is_turnip) && !is_suitable) {
+    if ((is_mvk || is_qualcomm || is_turnip || is_arm) && !is_suitable) {
         LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway");
     } else if (!is_suitable) {
         throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index b6d83e4468..7624a9b324 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -31,10 +31,34 @@
 
 namespace Vulkan {
 namespace {
+
+[[nodiscard]] bool AreExtensionsSupported(const vk::InstanceDispatch& dld,
+                                          std::span<const char* const> extensions) {
+    const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
+    if (!properties) {
+        LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
+        return false;
+    }
+    for (const char* extension : extensions) {
+        const auto it = std::ranges::find_if(*properties, [extension](const auto& prop) {
+            return std::strcmp(extension, prop.extensionName) == 0;
+        });
+        if (it == properties->end()) {
+            LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
+            return false;
+        }
+    }
+    return true;
+}
+
 [[nodiscard]] std::vector<const char*> RequiredExtensions(
-    Core::Frontend::WindowSystemType window_type, bool enable_validation) {
+    const vk::InstanceDispatch& dld, Core::Frontend::WindowSystemType window_type,
+    bool enable_validation) {
     std::vector<const char*> extensions;
     extensions.reserve(6);
+#ifdef __APPLE__
+    extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
+#endif
     switch (window_type) {
     case Core::Frontend::WindowSystemType::Headless:
         break;
@@ -66,35 +90,14 @@ namespace {
         extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
     }
     if (enable_validation) {
-        extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+        const bool debug_utils =
+            AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME});
+        extensions.push_back(debug_utils ? VK_EXT_DEBUG_UTILS_EXTENSION_NAME
+                                         : VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
     }
-    extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
-
-#ifdef __APPLE__
-    extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
-#endif
     return extensions;
 }
 
-[[nodiscard]] bool AreExtensionsSupported(const vk::InstanceDispatch& dld,
-                                          std::span<const char* const> extensions) {
-    const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
-    if (!properties) {
-        LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
-        return false;
-    }
-    for (const char* extension : extensions) {
-        const auto it = std::ranges::find_if(*properties, [extension](const auto& prop) {
-            return std::strcmp(extension, prop.extensionName) == 0;
-        });
-        if (it == properties->end()) {
-            LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
-            return false;
-        }
-    }
-    return true;
-}
-
 [[nodiscard]] std::vector<const char*> Layers(bool enable_validation) {
     std::vector<const char*> layers;
     if (enable_validation) {
@@ -138,7 +141,8 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
         LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
         throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
     }
-    const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_validation);
+    const std::vector<const char*> extensions =
+        RequiredExtensions(dld, window_type, enable_validation);
     if (!AreExtensionsSupported(dld, extensions)) {
         throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
     }
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 28fcb21a01..2fa29793ae 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -259,7 +259,9 @@ bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
     // These functions may fail to load depending on the enabled extensions.
     // Don't return a failure on these.
     X(vkCreateDebugUtilsMessengerEXT);
+    X(vkCreateDebugReportCallbackEXT);
     X(vkDestroyDebugUtilsMessengerEXT);
+    X(vkDestroyDebugReportCallbackEXT);
     X(vkDestroySurfaceKHR);
     X(vkGetPhysicalDeviceFeatures2);
     X(vkGetPhysicalDeviceProperties2);
@@ -481,6 +483,11 @@ void Destroy(VkInstance instance, VkDebugUtilsMessengerEXT handle,
     dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr);
 }
 
+void Destroy(VkInstance instance, VkDebugReportCallbackEXT handle,
+             const InstanceDispatch& dld) noexcept {
+    dld.vkDestroyDebugReportCallbackEXT(instance, handle, nullptr);
+}
+
 void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept {
     dld.vkDestroySurfaceKHR(instance, handle, nullptr);
 }
@@ -549,6 +556,13 @@ DebugUtilsMessenger Instance::CreateDebugUtilsMessenger(
     return DebugUtilsMessenger(object, handle, *dld);
 }
 
+DebugReportCallback Instance::CreateDebugReportCallback(
+    const VkDebugReportCallbackCreateInfoEXT& create_info) const {
+    VkDebugReportCallbackEXT object;
+    Check(dld->vkCreateDebugReportCallbackEXT(handle, &create_info, nullptr, &object));
+    return DebugReportCallback(object, handle, *dld);
+}
+
 void Image::SetObjectNameEXT(const char* name) const {
     SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name);
 }
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 44fce47a5a..b5e70fcd46 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -164,8 +164,10 @@ struct InstanceDispatch {
     PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{};
 
     PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{};
+    PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT{};
     PFN_vkCreateDevice vkCreateDevice{};
     PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{};
+    PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT{};
     PFN_vkDestroyDevice vkDestroyDevice{};
     PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{};
     PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{};
@@ -366,6 +368,7 @@ void Destroy(VkDevice, VkSwapchainKHR, const DeviceDispatch&) noexcept;
 void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept;
 void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept;
 void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept;
+void Destroy(VkInstance, VkDebugReportCallbackEXT, const InstanceDispatch&) noexcept;
 void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept;
 
 VkResult Free(VkDevice, VkDescriptorPool, Span<VkDescriptorSet>, const DeviceDispatch&) noexcept;
@@ -581,6 +584,7 @@ private:
 };
 
 using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>;
+using DebugReportCallback = Handle<VkDebugReportCallbackEXT, VkInstance, InstanceDispatch>;
 using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>;
 using DescriptorUpdateTemplate = Handle<VkDescriptorUpdateTemplate, VkDevice, DeviceDispatch>;
 using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>;
@@ -613,6 +617,11 @@ public:
     DebugUtilsMessenger CreateDebugUtilsMessenger(
         const VkDebugUtilsMessengerCreateInfoEXT& create_info) const;
 
+    /// Creates a debug report callback.
+    /// @throw Exception on creation failure.
+    DebugReportCallback CreateDebugReportCallback(
+        const VkDebugReportCallbackCreateInfoEXT& create_info) const;
+
     /// Returns dispatch table.
     const InstanceDispatch& Dispatch() const noexcept {
         return *dld;