Merge "BQ: Replace items from back of queue" into nyc-dev
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 6f57238..f29cb68 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <array>
 #include <inttypes.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include <log/log.h>
@@ -186,6 +187,58 @@
         AllocHandle(type, &device->next_handle[type]));
 }
 
+VKAPI_ATTR void* DefaultAllocate(void*,
+                                 size_t size,
+                                 size_t alignment,
+                                 VkSystemAllocationScope) {
+    void* ptr = nullptr;
+    // Vulkan requires 'alignment' to be a power of two, but posix_memalign
+    // additionally requires that it be at least sizeof(void*).
+    int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size);
+    return ret == 0 ? ptr : nullptr;
+}
+
+VKAPI_ATTR void* DefaultReallocate(void*,
+                                   void* ptr,
+                                   size_t size,
+                                   size_t alignment,
+                                   VkSystemAllocationScope) {
+    if (size == 0) {
+        free(ptr);
+        return nullptr;
+    }
+
+    // TODO(jessehall): Right now we never shrink allocations; if the new
+    // request is smaller than the existing chunk, we just continue using it.
+    // The null driver never reallocs, so this doesn't matter. If that changes,
+    // or if this code is copied into some other project, this should probably
+    // have a heuristic to allocate-copy-free when doing so will save "enough"
+    // space.
+    size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
+    if (size <= old_size)
+        return ptr;
+
+    void* new_ptr = nullptr;
+    if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0)
+        return nullptr;
+    if (ptr) {
+        memcpy(new_ptr, ptr, std::min(old_size, size));
+        free(ptr);
+    }
+    return new_ptr;
+}
+
+VKAPI_ATTR void DefaultFree(void*, void* ptr) {
+    free(ptr);
+}
+
+const VkAllocationCallbacks kDefaultAllocCallbacks = {
+    .pUserData = nullptr,
+    .pfnAllocation = DefaultAllocate,
+    .pfnReallocation = DefaultReallocate,
+    .pfnFree = DefaultFree,
+};
+
 }  // namespace
 
 namespace null_driver {
@@ -239,10 +292,8 @@
 VkResult CreateInstance(const VkInstanceCreateInfo* create_info,
                         const VkAllocationCallbacks* allocator,
                         VkInstance* out_instance) {
-    // Assume the loader provided alloc callbacks even if the app didn't.
-    ALOG_ASSERT(
-        allocator,
-        "Missing alloc callbacks, loader or app should have provided them");
+    if (!allocator)
+        allocator = &kDefaultAllocCallbacks;
 
     VkInstance_T* instance =
         static_cast<VkInstance_T*>(allocator->pfnAllocation(