Merge changes from topic 'vk-debug-report' into nyc-dev

* changes:
  vulkan: improve vulkan::driver logcat messages
  vulkan: use Logger in vulkan::api
  vulkan: use Logger in the generated code
  vulkan: add DebugReportLogger
  vulkan: make debug report callbacks available in DeviceData
  vulkan: avoid duplicated app messages
  vulkan: constify DebugReportCallbackList::Message
  vulkan: refactor DebugReportCallbackList
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 28b172d..8053668 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -394,7 +394,9 @@
                                               uint32_t& count);
 
    private:
-    LayerChain(bool is_instance, const VkAllocationCallbacks& allocator);
+    LayerChain(bool is_instance,
+               const driver::DebugReportLogger& logger,
+               const VkAllocationCallbacks& allocator);
     ~LayerChain();
 
     VkResult ActivateLayers(const char* const* layer_names,
@@ -455,6 +457,7 @@
                         void* user_data);
 
     const bool is_instance_;
+    const driver::DebugReportLogger& logger_;
     const VkAllocationCallbacks& allocator_;
 
     OverrideLayerNames override_layers_;
@@ -476,8 +479,11 @@
     std::bitset<driver::ProcHook::EXTENSION_COUNT> enabled_extensions_;
 };
 
-LayerChain::LayerChain(bool is_instance, const VkAllocationCallbacks& allocator)
+LayerChain::LayerChain(bool is_instance,
+                       const driver::DebugReportLogger& logger,
+                       const VkAllocationCallbacks& allocator)
     : is_instance_(is_instance),
+      logger_(logger),
       allocator_(allocator),
       override_layers_(is_instance, allocator),
       override_extensions_(is_instance, allocator),
@@ -562,12 +568,9 @@
         }
 
         if (!exact_match) {
-            ALOGW("Device layers");
-            for (uint32_t i = 0; i < layer_count; i++)
-                ALOGW("  %s", layer_names[i]);
-            ALOGW(
-                "disagree with instance layers and are overridden by "
-                "instance layers");
+            logger_.Warn(physical_dev,
+                         "Device layers disagree with instance layers and are "
+                         "overridden by instance layers");
         }
     }
 
@@ -617,7 +620,7 @@
 VkResult LayerChain::LoadLayer(ActiveLayer& layer, const char* name) {
     const Layer* l = FindLayer(name);
     if (!l) {
-        ALOGW("Failed to find layer %s", name);
+        logger_.Err(VK_NULL_HANDLE, "Failed to find layer %s", name);
         return VK_ERROR_LAYER_NOT_PRESENT;
     }
 
@@ -882,7 +885,8 @@
     for (uint32_t i = 0; i < extension_count; i++) {
         const char* name = extension_names[i];
         if (!IsLayerExtension(name) && !IsDriverExtension(name)) {
-            ALOGE("Failed to enable missing instance extension %s", name);
+            logger_.Err(VK_NULL_HANDLE,
+                        "Failed to enable missing instance extension %s", name);
             return VK_ERROR_EXTENSION_NOT_PRESENT;
         }
 
@@ -919,7 +923,8 @@
     for (uint32_t i = 0; i < extension_count; i++) {
         const char* name = extension_names[i];
         if (!IsLayerExtension(name) && !IsDriverExtension(name)) {
-            ALOGE("Failed to enable missing device extension %s", name);
+            logger_.Err(physical_dev,
+                        "Failed to enable missing device extension %s", name);
             return VK_ERROR_EXTENSION_NOT_PRESENT;
         }
 
@@ -1036,7 +1041,7 @@
 VkResult LayerChain::CreateInstance(const VkInstanceCreateInfo* create_info,
                                     const VkAllocationCallbacks* allocator,
                                     VkInstance* instance_out) {
-    LayerChain chain(true,
+    LayerChain chain(true, driver::DebugReportLogger(*create_info),
                      (allocator) ? *allocator : driver::GetDefaultAllocator());
 
     VkResult result = chain.ActivateLayers(create_info->ppEnabledLayerNames,
@@ -1061,9 +1066,9 @@
                                   const VkDeviceCreateInfo* create_info,
                                   const VkAllocationCallbacks* allocator,
                                   VkDevice* dev_out) {
-    LayerChain chain(false, (allocator)
-                                ? *allocator
-                                : driver::GetData(physical_dev).allocator);
+    LayerChain chain(
+        false, driver::Logger(physical_dev),
+        (allocator) ? *allocator : driver::GetData(physical_dev).allocator);
 
     VkResult result = chain.ActivateLayers(
         physical_dev, create_info->ppEnabledLayerNames,
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index fe4136f..155a599 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -51,56 +51,56 @@
 
 // clang-format off
 
-VKAPI_ATTR void disabledDestroySurfaceKHR(VkInstance, VkSurfaceKHR, const VkAllocationCallbacks*) {
-    ALOGE("VK_KHR_surface not enabled. vkDestroySurfaceKHR not executed.");
+VKAPI_ATTR void disabledDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR, const VkAllocationCallbacks*) {
+    driver::Logger(instance).Err(instance, "VK_KHR_surface not enabled. Exported vkDestroySurfaceKHR not executed.");
 }
 
-VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice, uint32_t, VkSurfaceKHR, VkBool32*) {
-    ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceSupportKHR not executed.");
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t, VkSurfaceKHR, VkBool32*) {
+    driver::Logger(physicalDevice).Err(physicalDevice, "VK_KHR_surface not enabled. Exported vkGetPhysicalDeviceSurfaceSupportKHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice, VkSurfaceKHR, VkSurfaceCapabilitiesKHR*) {
-    ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceCapabilitiesKHR not executed.");
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR, VkSurfaceCapabilitiesKHR*) {
+    driver::Logger(physicalDevice).Err(physicalDevice, "VK_KHR_surface not enabled. Exported vkGetPhysicalDeviceSurfaceCapabilitiesKHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkSurfaceFormatKHR*) {
-    ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceFormatsKHR not executed.");
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR, uint32_t*, VkSurfaceFormatKHR*) {
+    driver::Logger(physicalDevice).Err(physicalDevice, "VK_KHR_surface not enabled. Exported vkGetPhysicalDeviceSurfaceFormatsKHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkPresentModeKHR*) {
-    ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfacePresentModesKHR not executed.");
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR, uint32_t*, VkPresentModeKHR*) {
+    driver::Logger(physicalDevice).Err(physicalDevice, "VK_KHR_surface not enabled. Exported vkGetPhysicalDeviceSurfacePresentModesKHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledCreateSwapchainKHR(VkDevice, const VkSwapchainCreateInfoKHR*, const VkAllocationCallbacks*, VkSwapchainKHR*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
+VKAPI_ATTR VkResult disabledCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR*, const VkAllocationCallbacks*, VkSwapchainKHR*) {
+    driver::Logger(device).Err(device, "VK_KHR_swapchain not enabled. Exported vkCreateSwapchainKHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR void disabledDestroySwapchainKHR(VkDevice, VkSwapchainKHR, const VkAllocationCallbacks*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
+VKAPI_ATTR void disabledDestroySwapchainKHR(VkDevice device, VkSwapchainKHR, const VkAllocationCallbacks*) {
+    driver::Logger(device).Err(device, "VK_KHR_swapchain not enabled. Exported vkDestroySwapchainKHR not executed.");
 }
 
-VKAPI_ATTR VkResult disabledGetSwapchainImagesKHR(VkDevice, VkSwapchainKHR, uint32_t*, VkImage*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
+VKAPI_ATTR VkResult disabledGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR, uint32_t*, VkImage*) {
+    driver::Logger(device).Err(device, "VK_KHR_swapchain not enabled. Exported vkGetSwapchainImagesKHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledAcquireNextImageKHR(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
+VKAPI_ATTR VkResult disabledAcquireNextImageKHR(VkDevice device, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t*) {
+    driver::Logger(device).Err(device, "VK_KHR_swapchain not enabled. Exported vkAcquireNextImageKHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledQueuePresentKHR(VkQueue, const VkPresentInfoKHR*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
+VKAPI_ATTR VkResult disabledQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR*) {
+    driver::Logger(queue).Err(queue, "VK_KHR_swapchain not enabled. Exported vkQueuePresentKHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) {
-    ALOGE("VK_KHR_android_surface not enabled. vkCreateAndroidSurfaceKHR not executed.");
+VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) {
+    driver::Logger(instance).Err(instance, "VK_KHR_android_surface not enabled. Exported vkCreateAndroidSurfaceKHR not executed.");
     return VK_SUCCESS;
 }
 
@@ -304,7 +304,7 @@
 __attribute__((visibility("default")))
 VKAPI_ATTR PFN_vkVoidFunction vkGetDeviceProcAddr(VkDevice device, const char* pName) {
     if (device == VK_NULL_HANDLE) {
-        ALOGE("vkGetDeviceProcAddr called with invalid device");
+        ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call");
         return nullptr;
     }
 
@@ -342,7 +342,9 @@
         std::binary_search(
             known_non_device_names, known_non_device_names + count, pName,
             [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
-        ALOGE("vkGetDeviceProcAddr called with %s", (pName) ? pName : "(null)");
+        vulkan::driver::Logger(device).Err(
+            device, "invalid vkGetDeviceProcAddr(%p, \"%s\") call", device,
+            (pName) ? pName : "(null)");
         return nullptr;
     }
     // clang-format off
@@ -356,12 +358,12 @@
 __attribute__((visibility("default")))
 VKAPI_ATTR PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance instance, const char* pName) {
     // global functions
-    if (!instance) {
+    if (instance == VK_NULL_HANDLE) {
         if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::CreateInstance);
         if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::EnumerateInstanceLayerProperties);
         if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::EnumerateInstanceExtensionProperties);
 
-        ALOGE("vkGetInstanceProcAddr called with %s without instance",  pName);
+        ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \"%s\") call", pName);
         return nullptr;
     }
 
@@ -510,8 +512,11 @@
         hooks, hooks + count, pName,
         [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
     if (hook < hooks + count && strcmp(hook->name, pName) == 0) {
-        if (!hook->proc)
-            ALOGE("vkGetInstanceProcAddr called with %s with instance", pName);
+        if (!hook->proc) {
+            vulkan::driver::Logger(instance).Err(
+                instance, "invalid vkGetInstanceProcAddr(%p, \"%s\") call",
+                instance, pName);
+        }
         return hook->proc;
     }
     // clang-format off
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index 3968371..1f70549 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -482,10 +482,16 @@
     {{$ext_name := index $ext.Arguments 0}}
 
     {{$base := (Macro "BaseName" $)}}
-    {{$unnamed_params := (ForEach $.CallParameters "ParameterType" | JoinWith ", ")}}
 
-    VKAPI_ATTR {{Node "Type" $.Return}} disabled{{$base}}({{$unnamed_params}}) {
-      ALOGE("{{$ext_name}} not enabled. {{$.Name}} not executed.");
+    {{$p0 := (index $.CallParameters 0)}}
+    {{$ptail := (Tail 1 $.CallParameters)}}
+
+    {{$first_type := (Macro "Parameter" $p0)}}
+    {{$tail_types := (ForEach $ptail "ParameterType" | JoinWith ", ")}}
+
+    VKAPI_ATTR {{Node "Type" $.Return}} disabled{{$base}}({{$first_type}}, {{$tail_types}}) {
+      driver::Logger({{$p0.Name}}).Err({{$p0.Name}}, §
+        "{{$ext_name}} not enabled. Exported {{$.Name}} not executed.");
       {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
     }

@@ -502,7 +508,7 @@
   {{AssertType $ "API"}}
 
   // global functions
-  if (!instance) {
+  if (instance == VK_NULL_HANDLE) {
     {{range $f := AllCommands $}}
       {{if (Macro "IsGloballyDispatched" $f)}}
         if (strcmp(pName, "{{$f.Name}}") == 0) return §
@@ -511,7 +517,7 @@
       {{end}}
     {{end}}

-    ALOGE("vkGetInstanceProcAddr called with %s without instance",  pName);
+    ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \"%s\") call", pName);
     return nullptr;
   }

@@ -549,8 +555,11 @@
     hooks, hooks + count, pName,
     [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
   if (hook <  hooks + count && strcmp(hook->name, pName) == 0) {
-    if (!hook->proc)
-      ALOGE("vkGetInstanceProcAddr called with %s with instance",  pName);
+    if (!hook->proc) {
+      vulkan::driver::Logger(instance).Err(
+        instance, "invalid vkGetInstanceProcAddr(%p, \"%s\") call",
+        instance, pName);
+    }
     return hook->proc;
   }
   // clang-format off
@@ -567,7 +576,7 @@
   {{AssertType $ "API"}}
 
   if (device == VK_NULL_HANDLE) {
-    ALOGE("vkGetDeviceProcAddr called with invalid device");
+    ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call");
     return nullptr;
   }

@@ -587,7 +596,9 @@
       std::binary_search(
         known_non_device_names, known_non_device_names + count, pName,
         [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
-    ALOGE("vkGetDeviceProcAddr called with %s", (pName) ? pName : "(null)");
+    vulkan::driver::Logger(device).Err(§
+      device, "invalid vkGetDeviceProcAddr(%p, \"%s\") call", device,§
+      (pName) ? pName : "(null)");
     return nullptr;
   }
   // clang-format off
@@ -775,7 +786,6 @@
     {{$ext_name := index $ext.Arguments 0}}
 
     {{$base := (Macro "BaseName" $)}}
-    {{$unnamed_params := (ForEach $.CallParameters "ParameterType" | JoinWith ", ")}}
 
     VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) {
       {{$p0 := index $.CallParameters 0}}
@@ -785,7 +795,7 @@
         {{if not (IsVoid $.Return.Type)}}return §{{end}}
         {{$base}}({{Macro "Arguments" $}});
       } else {
-        ALOGE("{{$ext_name}} not enabled. {{$.Name}} not executed.");
+        Logger({{$p0.Name}}).Err({{$p0.Name}}, "{{$ext_name}} not enabled. {{$.Name}} not executed.");
         {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
       }
     }
diff --git a/vulkan/libvulkan/debug_report.cpp b/vulkan/libvulkan/debug_report.cpp
index c4a1174..2a34613 100644
--- a/vulkan/libvulkan/debug_report.cpp
+++ b/vulkan/libvulkan/debug_report.cpp
@@ -19,62 +19,37 @@
 namespace vulkan {
 namespace driver {
 
-VkResult DebugReportCallbackList::CreateCallback(
-    VkInstance instance,
-    const VkDebugReportCallbackCreateInfoEXT* create_info,
-    const VkAllocationCallbacks* allocator,
-    VkDebugReportCallbackEXT* callback) {
-    VkDebugReportCallbackEXT driver_callback = VK_NULL_HANDLE;
+DebugReportCallbackList::Node* DebugReportCallbackList::AddCallback(
+    const VkDebugReportCallbackCreateInfoEXT& info,
+    VkDebugReportCallbackEXT driver_handle,
+    const VkAllocationCallbacks& allocator) {
+    void* mem = allocator.pfnAllocation(allocator.pUserData, sizeof(Node),
+                                        alignof(Node),
+                                        VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+    if (!mem)
+        return nullptr;
 
-    if (GetData(instance).driver.CreateDebugReportCallbackEXT) {
-        VkResult result = GetData(instance).driver.CreateDebugReportCallbackEXT(
-            instance, create_info, allocator, &driver_callback);
-        if (result != VK_SUCCESS)
-            return result;
-    }
-
-    const VkAllocationCallbacks* alloc =
-        allocator ? allocator : &GetData(instance).allocator;
-    void* mem =
-        alloc->pfnAllocation(alloc->pUserData, sizeof(Node), alignof(Node),
-                             VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
-    if (!mem) {
-        if (GetData(instance).driver.DestroyDebugReportCallbackEXT) {
-            GetData(instance).driver.DestroyDebugReportCallbackEXT(
-                instance, driver_callback, allocator);
-        }
-        return VK_ERROR_OUT_OF_HOST_MEMORY;
-    }
-
+    // initialize and prepend node to the list
     std::lock_guard<decltype(rwmutex_)> lock(rwmutex_);
-    head_.next =
-        new (mem) Node{head_.next, create_info->flags, create_info->pfnCallback,
-                       create_info->pUserData, driver_callback};
-    *callback =
-        VkDebugReportCallbackEXT(reinterpret_cast<uintptr_t>(head_.next));
-    return VK_SUCCESS;
+    head_.next = new (mem) Node{head_.next, info.flags, info.pfnCallback,
+                                info.pUserData, driver_handle};
+
+    return head_.next;
 }
 
-void DebugReportCallbackList::DestroyCallback(
-    VkInstance instance,
-    VkDebugReportCallbackEXT callback,
-    const VkAllocationCallbacks* allocator) {
-    Node* node = reinterpret_cast<Node*>(uintptr_t(callback));
-    std::unique_lock<decltype(rwmutex_)> lock(rwmutex_);
-    Node* prev = &head_;
-    while (prev && prev->next != node)
-        prev = prev->next;
-    prev->next = node->next;
-    lock.unlock();
-
-    if (GetData(instance).driver.DestroyDebugReportCallbackEXT) {
-        GetData(instance).driver.DestroyDebugReportCallbackEXT(
-            instance, node->driver_callback, allocator);
+void DebugReportCallbackList::RemoveCallback(
+    Node* node,
+    const VkAllocationCallbacks& allocator) {
+    // remove node from the list
+    {
+        std::lock_guard<decltype(rwmutex_)> lock(rwmutex_);
+        Node* prev = &head_;
+        while (prev && prev->next != node)
+            prev = prev->next;
+        prev->next = node->next;
     }
 
-    const VkAllocationCallbacks* alloc =
-        allocator ? allocator : &GetData(instance).allocator;
-    alloc->pfnFree(alloc->pUserData, node);
+    allocator.pfnFree(allocator.pUserData, node);
 }
 
 void DebugReportCallbackList::Message(VkDebugReportFlagsEXT flags,
@@ -83,32 +58,108 @@
                                       size_t location,
                                       int32_t message_code,
                                       const char* layer_prefix,
-                                      const char* message) {
+                                      const char* message) const {
     std::shared_lock<decltype(rwmutex_)> lock(rwmutex_);
-    Node* node = &head_;
+    const Node* node = &head_;
     while ((node = node->next)) {
         if ((node->flags & flags) != 0) {
             node->callback(flags, object_type, object, location, message_code,
-                           layer_prefix, message, node->data);
+                           layer_prefix, message, node->user_data);
         }
     }
 }
 
+void DebugReportLogger::Message(VkDebugReportFlagsEXT flags,
+                                VkDebugReportObjectTypeEXT object_type,
+                                uint64_t object,
+                                size_t location,
+                                int32_t message_code,
+                                const char* layer_prefix,
+                                const char* message) const {
+    const VkDebugReportCallbackCreateInfoEXT* info =
+        reinterpret_cast<const VkDebugReportCallbackCreateInfoEXT*>(
+            instance_pnext_);
+    while (info) {
+        if (info->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) {
+            info->pfnCallback(flags, object_type, object, location,
+                              message_code, layer_prefix, message,
+                              info->pUserData);
+        }
+
+        info = reinterpret_cast<const VkDebugReportCallbackCreateInfoEXT*>(
+            info->pNext);
+    }
+
+    if (callbacks_) {
+        callbacks_->Message(flags, object_type, object, location, message_code,
+                            layer_prefix, message);
+    }
+}
+
+void DebugReportLogger::PrintV(VkDebugReportFlagsEXT flags,
+                               VkDebugReportObjectTypeEXT object_type,
+                               uint64_t object,
+                               const char* format,
+                               va_list ap) const {
+    char buf[1024];
+    int len = vsnprintf(buf, sizeof(buf), format, ap);
+
+    // message truncated
+    if (len >= static_cast<int>(sizeof(buf)))
+        memcpy(buf + sizeof(buf) - 4, "...", 4);
+
+    Message(flags, object_type, object, 0, 0, LOG_TAG, buf);
+}
+
 VkResult CreateDebugReportCallbackEXT(
     VkInstance instance,
     const VkDebugReportCallbackCreateInfoEXT* create_info,
     const VkAllocationCallbacks* allocator,
     VkDebugReportCallbackEXT* callback) {
-    return GetData(instance).debug_report_callbacks.CreateCallback(
-        instance, create_info, allocator, callback);
+    const auto& driver = GetData(instance).driver;
+    VkDebugReportCallbackEXT driver_handle = VK_NULL_HANDLE;
+    if (driver.CreateDebugReportCallbackEXT) {
+        VkResult result = driver.CreateDebugReportCallbackEXT(
+            instance, create_info, allocator, &driver_handle);
+        if (result != VK_SUCCESS)
+            return result;
+    }
+
+    auto& callbacks = GetData(instance).debug_report_callbacks;
+    auto node = callbacks.AddCallback(
+        *create_info, driver_handle,
+        (allocator) ? *allocator : GetData(instance).allocator);
+    if (!node) {
+        if (driver_handle != VK_NULL_HANDLE) {
+            driver.DestroyDebugReportCallbackEXT(instance, driver_handle,
+                                                 allocator);
+        }
+
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+
+    *callback = callbacks.GetHandle(node);
+
+    return VK_SUCCESS;
 }
 
 void DestroyDebugReportCallbackEXT(VkInstance instance,
                                    VkDebugReportCallbackEXT callback,
                                    const VkAllocationCallbacks* allocator) {
-    if (callback)
-        GetData(instance).debug_report_callbacks.DestroyCallback(
-            instance, callback, allocator);
+    if (callback == VK_NULL_HANDLE)
+        return;
+
+    auto& callbacks = GetData(instance).debug_report_callbacks;
+    auto node = callbacks.FromHandle(callback);
+    auto driver_handle = callbacks.GetDriverHandle(node);
+
+    callbacks.RemoveCallback(
+        node, (allocator) ? *allocator : GetData(instance).allocator);
+
+    if (driver_handle != VK_NULL_HANDLE) {
+        GetData(instance).driver.DestroyDebugReportCallbackEXT(
+            instance, driver_handle, allocator);
+    }
 }
 
 void DebugReportMessageEXT(VkInstance instance,
@@ -123,10 +174,11 @@
         GetData(instance).driver.DebugReportMessageEXT(
             instance, flags, object_type, object, location, message_code,
             layer_prefix, message);
+    } else {
+        GetData(instance).debug_report_callbacks.Message(
+            flags, object_type, object, location, message_code, layer_prefix,
+            message);
     }
-    GetData(instance).debug_report_callbacks.Message(flags, object_type, object,
-                                                     location, message_code,
-                                                     layer_prefix, message);
 }
 
 }  // namespace driver
diff --git a/vulkan/libvulkan/debug_report.h b/vulkan/libvulkan/debug_report.h
index 72b1887..be9b645 100644
--- a/vulkan/libvulkan/debug_report.h
+++ b/vulkan/libvulkan/debug_report.h
@@ -17,8 +17,9 @@
 #ifndef LIBVULKAN_DEBUG_REPORT_H
 #define LIBVULKAN_DEBUG_REPORT_H 1
 
-#include <vulkan/vulkan.h>
+#include <stdarg.h>
 #include <shared_mutex>
+#include <vulkan/vulkan.h>
 
 namespace vulkan {
 namespace driver {
@@ -30,6 +31,10 @@
 // clang-format on
 
 class DebugReportCallbackList {
+   private:
+    // forward declaration
+    struct Node;
+
    public:
     DebugReportCallbackList()
         : head_{nullptr, 0, nullptr, nullptr, VK_NULL_HANDLE} {}
@@ -37,36 +42,124 @@
     DebugReportCallbackList& operator=(const DebugReportCallbackList&) = delete;
     ~DebugReportCallbackList() = default;
 
-    VkResult CreateCallback(
-        VkInstance instance,
-        const VkDebugReportCallbackCreateInfoEXT* create_info,
-        const VkAllocationCallbacks* allocator,
-        VkDebugReportCallbackEXT* callback);
-    void DestroyCallback(VkInstance instance,
-                         VkDebugReportCallbackEXT callback,
-                         const VkAllocationCallbacks* allocator);
+    Node* AddCallback(const VkDebugReportCallbackCreateInfoEXT& info,
+                      VkDebugReportCallbackEXT driver_handle,
+                      const VkAllocationCallbacks& allocator);
+    void RemoveCallback(Node* node, const VkAllocationCallbacks& allocator);
+
     void Message(VkDebugReportFlagsEXT flags,
                  VkDebugReportObjectTypeEXT object_type,
                  uint64_t object,
                  size_t location,
                  int32_t message_code,
                  const char* layer_prefix,
-                 const char* message);
+                 const char* message) const;
+
+    static Node* FromHandle(VkDebugReportCallbackEXT handle) {
+        return reinterpret_cast<Node*>(uintptr_t(handle));
+    }
+
+    static VkDebugReportCallbackEXT GetHandle(const Node* node) {
+        return VkDebugReportCallbackEXT(reinterpret_cast<uintptr_t>(node));
+    }
+
+    static VkDebugReportCallbackEXT GetDriverHandle(const Node* node) {
+        return node->driver_handle;
+    }
 
    private:
     struct Node {
         Node* next;
+
         VkDebugReportFlagsEXT flags;
         PFN_vkDebugReportCallbackEXT callback;
-        void* data;
-        VkDebugReportCallbackEXT driver_callback;
+        void* user_data;
+
+        VkDebugReportCallbackEXT driver_handle;
     };
 
     // TODO(jessehall): replace with std::shared_mutex when available in libc++
-    std::shared_timed_mutex rwmutex_;
+    mutable std::shared_timed_mutex rwmutex_;
     Node head_;
 };
 
+class DebugReportLogger {
+   public:
+    DebugReportLogger(const VkInstanceCreateInfo& info)
+        : instance_pnext_(info.pNext), callbacks_(nullptr) {}
+    DebugReportLogger(const DebugReportCallbackList& callbacks)
+        : instance_pnext_(nullptr), callbacks_(&callbacks) {}
+
+    void Message(VkDebugReportFlagsEXT flags,
+                 VkDebugReportObjectTypeEXT object_type,
+                 uint64_t object,
+                 size_t location,
+                 int32_t message_code,
+                 const char* layer_prefix,
+                 const char* message) const;
+
+#define DEBUG_REPORT_LOGGER_PRINTF(fmt, args) \
+    __attribute__((format(printf, (fmt) + 1, (args) + 1)))
+    template <typename ObjectType>
+    void Info(ObjectType object, const char* format, ...) const
+        DEBUG_REPORT_LOGGER_PRINTF(2, 3) {
+        va_list ap;
+        va_start(ap, format);
+        PrintV(VK_DEBUG_REPORT_INFORMATION_BIT_EXT, GetObjectType(object),
+               GetObjectUInt64(object), format, ap);
+        va_end(ap);
+    }
+
+    template <typename ObjectType>
+    void Warn(ObjectType object, const char* format, ...) const
+        DEBUG_REPORT_LOGGER_PRINTF(2, 3) {
+        va_list ap;
+        va_start(ap, format);
+        PrintV(VK_DEBUG_REPORT_WARNING_BIT_EXT, GetObjectType(object),
+               GetObjectUInt64(object), format, ap);
+        va_end(ap);
+    }
+
+    template <typename ObjectType>
+    void Err(ObjectType object, const char* format, ...) const
+        DEBUG_REPORT_LOGGER_PRINTF(2, 3) {
+        va_list ap;
+        va_start(ap, format);
+        PrintV(VK_DEBUG_REPORT_ERROR_BIT_EXT, GetObjectType(object),
+               GetObjectUInt64(object), format, ap);
+        va_end(ap);
+    }
+
+   private:
+    template <typename ObjectType>
+    static VkDebugReportObjectTypeEXT GetObjectType(ObjectType) {
+        if (std::is_same<ObjectType, VkInstance>::value)
+            return VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT;
+        else if (std::is_same<ObjectType, VkPhysicalDevice>::value)
+            return VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT;
+        else if (std::is_same<ObjectType, VkDevice>::value)
+            return VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT;
+        else
+            return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;
+    }
+
+    template <typename ObjectType>
+    static uint64_t GetObjectUInt64(ObjectType object) {
+        return uint64_t(object);
+    }
+
+#define DEBUG_REPORT_LOGGER_VPRINTF(fmt) \
+    __attribute__((format(printf, (fmt) + 1, 0)))
+    void PrintV(VkDebugReportFlagsEXT flags,
+                VkDebugReportObjectTypeEXT object_type,
+                uint64_t object,
+                const char* format,
+                va_list ap) const DEBUG_REPORT_LOGGER_VPRINTF(4);
+
+    const void* const instance_pnext_;
+    const DebugReportCallbackList* const callbacks_;
+};
+
 }  // namespace driver
 }  // namespace vulkan
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index b02f5b4..0e64b24 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -409,14 +409,16 @@
     allocator.pfnFree(allocator.pUserData, data);
 }
 
-DeviceData* AllocateDeviceData(const VkAllocationCallbacks& allocator) {
+DeviceData* AllocateDeviceData(
+    const VkAllocationCallbacks& allocator,
+    const DebugReportCallbackList& debug_report_callbacks) {
     void* data_mem = allocator.pfnAllocation(
         allocator.pUserData, sizeof(DeviceData), alignof(DeviceData),
         VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
     if (!data_mem)
         return nullptr;
 
-    return new (data_mem) DeviceData(allocator);
+    return new (data_mem) DeviceData(allocator, debug_report_callbacks);
 }
 
 void FreeDeviceData(DeviceData* data, const VkAllocationCallbacks& allocator) {
@@ -440,7 +442,7 @@
     int result =
         hw_get_module("vulkan", reinterpret_cast<const hw_module_t**>(&module));
     if (result != 0) {
-        ALOGV("no Vulkan HAL present, using stub HAL");
+        ALOGI("no Vulkan HAL present, using stub HAL");
         return true;
     }
 
@@ -489,8 +491,7 @@
             return hook->proc;
 
         ALOGE(
-            "Invalid use of vkGetInstanceProcAddr to query %s without an "
-            "instance",
+            "internal vkGetInstanceProcAddr called for %s without an instance",
             pName);
 
         return nullptr;
@@ -511,8 +512,7 @@
             break;
         default:
             ALOGE(
-                "Invalid use of vkGetInstanceProcAddr to query %s with an "
-                "instance",
+                "internal vkGetInstanceProcAddr called for %s with an instance",
                 pName);
             proc = nullptr;
             break;
@@ -527,7 +527,7 @@
         return GetData(device).driver.GetDeviceProcAddr(device, pName);
 
     if (hook->type != ProcHook::DEVICE) {
-        ALOGE("Invalid use of vkGetDeviceProcAddr to query %s", pName);
+        ALOGE("internal vkGetDeviceProcAddr called for %s", pName);
         return nullptr;
     }
 
@@ -684,7 +684,8 @@
     if (result != VK_SUCCESS)
         return result;
 
-    DeviceData* data = AllocateDeviceData(data_allocator);
+    DeviceData* data = AllocateDeviceData(data_allocator,
+                                          instance_data.debug_report_callbacks);
     if (!data)
         return VK_ERROR_OUT_OF_HOST_MEMORY;
 
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 2b1f545..210c3c7 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -82,14 +82,19 @@
 };
 
 struct DeviceData {
-    DeviceData(const VkAllocationCallbacks& alloc)
-        : opaque_api_data(), allocator(alloc), driver() {
+    DeviceData(const VkAllocationCallbacks& alloc,
+               const DebugReportCallbackList& debug_report_callbacks_)
+        : opaque_api_data(),
+          allocator(alloc),
+          debug_report_callbacks(debug_report_callbacks_),
+          driver() {
         hook_extensions.set(ProcHook::EXTENSION_CORE);
     }
 
     api::DeviceData opaque_api_data;
 
     const VkAllocationCallbacks allocator;
+    const DebugReportCallbackList& debug_report_callbacks;
 
     std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
 
@@ -214,6 +219,11 @@
     return *reinterpret_cast<DeviceData*>(GetDataInternal(dispatchable));
 }
 
+template <typename DispatchableType>
+const DebugReportLogger Logger(DispatchableType dispatchable) {
+    return DebugReportLogger(GetData(dispatchable).debug_report_callbacks);
+}
+
 }  // namespace driver
 }  // namespace vulkan
 
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 5bd2159..29351a1 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -33,7 +33,7 @@
     if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
         return CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
     } else {
-        ALOGE("VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
+        Logger(device).Err(device, "VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
         return VK_SUCCESS;
     }
 }
@@ -42,7 +42,7 @@
     if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
         DestroySwapchainKHR(device, swapchain, pAllocator);
     } else {
-        ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
+        Logger(device).Err(device, "VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
     }
 }
 
@@ -50,7 +50,7 @@
     if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
         return GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
     } else {
-        ALOGE("VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
+        Logger(device).Err(device, "VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
         return VK_SUCCESS;
     }
 }
@@ -59,7 +59,7 @@
     if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
         return AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
     } else {
-        ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
+        Logger(device).Err(device, "VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
         return VK_SUCCESS;
     }
 }
@@ -68,7 +68,7 @@
     if (GetData(queue).hook_extensions[ProcHook::KHR_swapchain]) {
         return QueuePresentKHR(queue, pPresentInfo);
     } else {
-        ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
+        Logger(queue).Err(queue, "VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
         return VK_SUCCESS;
     }
 }