vulkan: initial loader and null driver

Change-Id: Id5ebb5f01e61e9b114990f49c64c88fbbb7b730e
(cherry picked from commit 4df205cdfc61e66de774ba50be9ef59a08cf88bb)
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
new file mode 100644
index 0000000..a99e90a
--- /dev/null
+++ b/vulkan/libvulkan/loader.cpp
@@ -0,0 +1,532 @@
+// module header
+#include "loader.h"
+// standard C headers
+#include <inttypes.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <string.h>
+// standard C++ headers
+#include <algorithm>
+#include <mutex>
+// platform/library headers
+#include <hardware/hwvulkan.h>
+#include <log/log.h>
+
+using namespace vulkan;
+
+static const uint32_t kMaxPhysicalDevices = 4;
+
+struct VkInstance_T {
+    VkInstance_T(const VkAllocCallbacks* alloc_callbacks)
+        : vtbl(&vtbl_storage), alloc(alloc_callbacks), num_physical_devices(0) {
+        memset(&vtbl_storage, 0, sizeof(vtbl_storage));
+        memset(physical_devices, 0, sizeof(physical_devices));
+        memset(&drv.vtbl, 0, sizeof(drv.vtbl));
+        drv.GetDeviceProcAddr = nullptr;
+        drv.num_physical_devices = 0;
+    }
+
+    InstanceVtbl* vtbl;
+    InstanceVtbl vtbl_storage;
+
+    const VkAllocCallbacks* alloc;
+    uint32_t num_physical_devices;
+    VkPhysicalDevice physical_devices[kMaxPhysicalDevices];
+
+    struct Driver {
+        // Pointers to driver entry points. Used explicitly by the loader; not
+        // set as the dispatch table for any objects.
+        InstanceVtbl vtbl;
+
+        // Pointer to the driver's get_device_proc_addr, must be valid for any
+        // of the driver's physical devices. Not part of the InstanceVtbl since
+        // it's not an Instance/PhysicalDevice function.
+        PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
+
+        // Number of physical devices owned by this driver.
+        uint32_t num_physical_devices;
+    } drv;  // may eventually be an array
+};
+
+// -----------------------------------------------------------------------------
+
+namespace {
+
+typedef VkInstance_T Instance;
+
+struct Device {
+    Device(const VkAllocCallbacks* alloc_callbacks) : alloc(alloc_callbacks) {
+        memset(&vtbl_storage, 0, sizeof(vtbl_storage));
+        vtbl_storage.device = this;
+    }
+    DeviceVtbl vtbl_storage;
+    const VkAllocCallbacks* alloc;
+};
+
+// -----------------------------------------------------------------------------
+// Utility Code
+
+inline const InstanceVtbl* GetVtbl(VkPhysicalDevice physicalDevice) {
+    return *reinterpret_cast<InstanceVtbl**>(physicalDevice);
+}
+
+inline const DeviceVtbl* GetVtbl(VkDevice device) {
+    return *reinterpret_cast<DeviceVtbl**>(device);
+}
+
+void* DefaultAlloc(void*, size_t size, size_t alignment, VkSystemAllocType) {
+    return memalign(alignment, size);
+}
+
+void DefaultFree(void*, void* pMem) {
+    free(pMem);
+}
+
+const VkAllocCallbacks kDefaultAllocCallbacks = {
+    .pUserData = nullptr,
+    .pfnAlloc = DefaultAlloc,
+    .pfnFree = DefaultFree,
+};
+
+hwvulkan_device_t* g_hwdevice;
+bool EnsureInitialized() {
+    static std::once_flag once_flag;
+    static const hwvulkan_module_t* module;
+
+    std::call_once(once_flag, []() {
+        int result;
+        result = hw_get_module("vulkan",
+                               reinterpret_cast<const hw_module_t**>(&module));
+        if (result != 0) {
+            ALOGE("failed to load vulkan hal: %s (%d)", strerror(-result),
+                  result);
+            return;
+        }
+        result = module->common.methods->open(
+            &module->common, HWVULKAN_DEVICE_0,
+            reinterpret_cast<hw_device_t**>(&g_hwdevice));
+        if (result != 0) {
+            ALOGE("failed to open vulkan driver: %s (%d)", strerror(-result),
+                  result);
+            module = nullptr;
+            return;
+        }
+    });
+
+    return module != nullptr && g_hwdevice != nullptr;
+}
+
+void DestroyDevice(Device* device) {
+    const VkAllocCallbacks* alloc = device->alloc;
+    device->~Device();
+    alloc->pfnFree(alloc->pUserData, device);
+}
+
+// -----------------------------------------------------------------------------
+// "Bottom" functions. These are called at the end of the instance dispatch
+// chain.
+
+VkResult DestroyInstanceBottom(VkInstance instance) {
+    // These checks allow us to call DestroyInstanceBottom from any error path
+    // in CreateInstanceBottom, before the driver instance is fully initialized.
+    if (instance->drv.vtbl.instance != VK_NULL_HANDLE &&
+        instance->drv.vtbl.DestroyInstance) {
+        instance->drv.vtbl.DestroyInstance(instance->drv.vtbl.instance);
+    }
+    const VkAllocCallbacks* alloc = instance->alloc;
+    instance->~VkInstance_T();
+    alloc->pfnFree(alloc->pUserData, instance);
+    return VK_SUCCESS;
+}
+
+VkResult CreateInstanceBottom(const VkInstanceCreateInfo* create_info,
+                              VkInstance* instance_ptr) {
+    Instance* instance = *instance_ptr;
+    VkResult result;
+
+    result =
+        g_hwdevice->CreateInstance(create_info, &instance->drv.vtbl.instance);
+    if (result != VK_SUCCESS) {
+        DestroyInstanceBottom(instance);
+        return result;
+    }
+
+    if (!LoadInstanceVtbl(instance->drv.vtbl.instance,
+                          g_hwdevice->GetInstanceProcAddr,
+                          instance->drv.vtbl)) {
+        DestroyInstanceBottom(instance);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    // vkGetDeviceProcAddr has a bootstrapping problem. We require that it be
+    // queryable from the Instance, and that the resulting function work for any
+    // VkDevice created from the instance.
+    instance->drv.GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
+        g_hwdevice->GetInstanceProcAddr(instance->drv.vtbl.instance,
+                                        "vkGetDeviceProcAddr"));
+    if (!instance->drv.GetDeviceProcAddr) {
+        ALOGE("missing instance proc: \"%s\"", "vkGetDeviceProcAddr");
+        DestroyInstanceBottom(instance);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    hwvulkan_dispatch_t* dispatch =
+        reinterpret_cast<hwvulkan_dispatch_t*>(instance->drv.vtbl.instance);
+    if (dispatch->magic == HWVULKAN_DISPATCH_MAGIC) {
+        // Skip setting dispatch->vtbl on the driver instance handle, since we
+        // never intentionally call through it; we go through Instance::drv.vtbl
+        // instead.
+    } else {
+        ALOGE("invalid VkInstance dispatch magic: 0x%" PRIxPTR,
+              dispatch->magic);
+        DestroyInstanceBottom(instance);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    uint32_t num_physical_devices = 0;
+    result = instance->drv.vtbl.EnumeratePhysicalDevices(
+        instance->drv.vtbl.instance, &num_physical_devices, nullptr);
+    if (result != VK_SUCCESS) {
+        DestroyInstanceBottom(instance);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+    num_physical_devices = std::min(num_physical_devices, kMaxPhysicalDevices);
+    result = instance->drv.vtbl.EnumeratePhysicalDevices(
+        instance->drv.vtbl.instance, &num_physical_devices,
+        instance->physical_devices);
+    if (result != VK_SUCCESS) {
+        DestroyInstanceBottom(instance);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+    for (uint32_t i = 0; i < num_physical_devices; i++) {
+        dispatch = reinterpret_cast<hwvulkan_dispatch_t*>(
+            instance->physical_devices[i]);
+        if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
+            ALOGE("invalid VkPhysicalDevice dispatch magic: 0x%" PRIxPTR,
+                  dispatch->magic);
+            DestroyInstanceBottom(instance);
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+        dispatch->vtbl = instance->vtbl;
+    }
+    instance->drv.num_physical_devices = num_physical_devices;
+
+    instance->num_physical_devices = instance->drv.num_physical_devices;
+    return VK_SUCCESS;
+}
+
+VkResult EnumeratePhysicalDevicesBottom(VkInstance instance,
+                                        uint32_t* pdev_count,
+                                        VkPhysicalDevice* pdevs) {
+    uint32_t count = instance->num_physical_devices;
+    if (pdevs) {
+        count = std::min(count, *pdev_count);
+        std::copy(instance->physical_devices,
+                  instance->physical_devices + count, pdevs);
+    }
+    *pdev_count = count;
+    return VK_SUCCESS;
+}
+
+VkResult GetPhysicalDeviceFeaturesBottom(VkPhysicalDevice pdev,
+                                         VkPhysicalDeviceFeatures* features) {
+    return GetVtbl(pdev)
+        ->instance->drv.vtbl.GetPhysicalDeviceFeatures(pdev, features);
+}
+
+VkResult GetPhysicalDeviceFormatPropertiesBottom(
+    VkPhysicalDevice pdev,
+    VkFormat format,
+    VkFormatProperties* properties) {
+    return GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceFormatProperties(
+        pdev, format, properties);
+}
+
+VkResult GetPhysicalDeviceImageFormatPropertiesBottom(
+    VkPhysicalDevice pdev,
+    VkFormat format,
+    VkImageType type,
+    VkImageTiling tiling,
+    VkImageUsageFlags usage,
+    VkImageFormatProperties* properties) {
+    return GetVtbl(pdev)
+        ->instance->drv.vtbl.GetPhysicalDeviceImageFormatProperties(
+            pdev, format, type, tiling, usage, properties);
+}
+
+VkResult GetPhysicalDeviceLimitsBottom(VkPhysicalDevice pdev,
+                                       VkPhysicalDeviceLimits* limits) {
+    return GetVtbl(pdev)
+        ->instance->drv.vtbl.GetPhysicalDeviceLimits(pdev, limits);
+}
+
+VkResult GetPhysicalDevicePropertiesBottom(
+    VkPhysicalDevice pdev,
+    VkPhysicalDeviceProperties* properties) {
+    return GetVtbl(pdev)
+        ->instance->drv.vtbl.GetPhysicalDeviceProperties(pdev, properties);
+}
+
+VkResult GetPhysicalDeviceQueueCountBottom(VkPhysicalDevice pdev,
+                                           uint32_t* count) {
+    return GetVtbl(pdev)
+        ->instance->drv.vtbl.GetPhysicalDeviceQueueCount(pdev, count);
+}
+
+VkResult GetPhysicalDeviceQueuePropertiesBottom(
+    VkPhysicalDevice pdev,
+    uint32_t count,
+    VkPhysicalDeviceQueueProperties* properties) {
+    return GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceQueueProperties(
+        pdev, count, properties);
+}
+
+VkResult GetPhysicalDeviceMemoryPropertiesBottom(
+    VkPhysicalDevice pdev,
+    VkPhysicalDeviceMemoryProperties* properties) {
+    return GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceMemoryProperties(
+        pdev, properties);
+}
+
+VkResult CreateDeviceBottom(VkPhysicalDevice pdev,
+                            const VkDeviceCreateInfo* create_info,
+                            VkDevice* out_device) {
+    const Instance& instance = *static_cast<Instance*>(GetVtbl(pdev)->instance);
+    VkResult result;
+
+    void* mem = instance.alloc->pfnAlloc(instance.alloc->pUserData,
+                                         sizeof(Device), alignof(Device),
+                                         VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
+    if (!mem)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+    Device* device = new (mem) Device(instance.alloc);
+
+    VkDevice drv_device;
+    result = instance.drv.vtbl.CreateDevice(pdev, create_info, &drv_device);
+    if (result != VK_SUCCESS) {
+        DestroyDevice(device);
+        return result;
+    }
+
+    if (!LoadDeviceVtbl(drv_device, instance.drv.GetDeviceProcAddr,
+                        device->vtbl_storage)) {
+        if (device->vtbl_storage.DestroyDevice)
+            device->vtbl_storage.DestroyDevice(drv_device);
+        DestroyDevice(device);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    hwvulkan_dispatch_t* dispatch =
+        reinterpret_cast<hwvulkan_dispatch_t*>(drv_device);
+    if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
+        ALOGE("invalid VkDevice dispatch magic: 0x%" PRIxPTR, dispatch->magic);
+        device->vtbl_storage.DestroyDevice(drv_device);
+        DestroyDevice(device);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+    dispatch->vtbl = &device->vtbl_storage;
+
+    // TODO: insert device layer entry points into device->vtbl_storage here?
+
+    *out_device = drv_device;
+    return VK_SUCCESS;
+}
+
+VkResult GetPhysicalDeviceExtensionPropertiesBottom(
+    VkPhysicalDevice pdev,
+    const char* layer_name,
+    uint32_t* properties_count,
+    VkExtensionProperties* properties) {
+    // TODO: what are we supposed to do with layer_name here?
+    return GetVtbl(pdev)
+        ->instance->drv.vtbl.GetPhysicalDeviceExtensionProperties(
+            pdev, layer_name, properties_count, properties);
+}
+
+VkResult GetPhysicalDeviceLayerPropertiesBottom(VkPhysicalDevice pdev,
+                                                uint32_t* properties_count,
+                                                VkLayerProperties* properties) {
+    return GetVtbl(pdev)->instance->drv.vtbl.GetPhysicalDeviceLayerProperties(
+        pdev, properties_count, properties);
+}
+
+VkResult GetPhysicalDeviceSparseImageFormatPropertiesBottom(
+    VkPhysicalDevice pdev,
+    VkFormat format,
+    VkImageType type,
+    uint32_t samples,
+    VkImageUsageFlags usage,
+    VkImageTiling tiling,
+    uint32_t* properties_count,
+    VkSparseImageFormatProperties* properties) {
+    return GetVtbl(pdev)
+        ->instance->drv.vtbl.GetPhysicalDeviceSparseImageFormatProperties(
+            pdev, format, type, samples, usage, tiling, properties_count,
+            properties);
+}
+
+PFN_vkVoidFunction GetInstanceProcAddrBottom(VkInstance, const char*);
+
+const InstanceVtbl kBottomInstanceFunctions = {
+    // clang-format off
+    .instance = nullptr,
+    .CreateInstance = CreateInstanceBottom,
+    .DestroyInstance = DestroyInstanceBottom,
+    .GetInstanceProcAddr = GetInstanceProcAddrBottom,
+    .EnumeratePhysicalDevices = EnumeratePhysicalDevicesBottom,
+    .GetPhysicalDeviceFeatures = GetPhysicalDeviceFeaturesBottom,
+    .GetPhysicalDeviceFormatProperties = GetPhysicalDeviceFormatPropertiesBottom,
+    .GetPhysicalDeviceImageFormatProperties = GetPhysicalDeviceImageFormatPropertiesBottom,
+    .GetPhysicalDeviceLimits = GetPhysicalDeviceLimitsBottom,
+    .GetPhysicalDeviceProperties = GetPhysicalDevicePropertiesBottom,
+    .GetPhysicalDeviceQueueCount = GetPhysicalDeviceQueueCountBottom,
+    .GetPhysicalDeviceQueueProperties = GetPhysicalDeviceQueuePropertiesBottom,
+    .GetPhysicalDeviceMemoryProperties = GetPhysicalDeviceMemoryPropertiesBottom,
+    .CreateDevice = CreateDeviceBottom,
+    .GetPhysicalDeviceExtensionProperties = GetPhysicalDeviceExtensionPropertiesBottom,
+    .GetPhysicalDeviceLayerProperties = GetPhysicalDeviceLayerPropertiesBottom,
+    .GetPhysicalDeviceSparseImageFormatProperties = GetPhysicalDeviceSparseImageFormatPropertiesBottom,
+    // clang-format on
+};
+
+PFN_vkVoidFunction GetInstanceProcAddrBottom(VkInstance, const char* name) {
+    // The bottom GetInstanceProcAddr is only called by the innermost layer,
+    // when there is one, when it initializes its own dispatch table.
+    return GetSpecificInstanceProcAddr(&kBottomInstanceFunctions, name);
+}
+
+}  // namespace
+
+// -----------------------------------------------------------------------------
+// Global functions. These are called directly from the loader entry points,
+// without going through a dispatch table.
+
+namespace vulkan {
+
+VkResult GetGlobalExtensionProperties(const char* /*layer_name*/,
+                                      uint32_t* count,
+                                      VkExtensionProperties* /*properties*/) {
+    if (!count)
+        return VK_ERROR_INVALID_POINTER;
+    if (!EnsureInitialized())
+        return VK_ERROR_UNAVAILABLE;
+
+    // TODO: not yet implemented
+    ALOGW("vkGetGlobalExtensionProperties not implemented");
+
+    *count = 0;
+    return VK_SUCCESS;
+}
+
+VkResult GetGlobalLayerProperties(uint32_t* count,
+                                  VkLayerProperties* /*properties*/) {
+    if (!count)
+        return VK_ERROR_INVALID_POINTER;
+    if (!EnsureInitialized())
+        return VK_ERROR_UNAVAILABLE;
+
+    // TODO: not yet implemented
+    ALOGW("vkGetGlobalLayerProperties not implemented");
+
+    *count = 0;
+    return VK_SUCCESS;
+}
+
+VkResult CreateInstance(const VkInstanceCreateInfo* create_info,
+                        VkInstance* out_instance) {
+    VkResult result;
+
+    if (!EnsureInitialized())
+        return VK_ERROR_UNAVAILABLE;
+
+    VkInstanceCreateInfo local_create_info = *create_info;
+    if (!local_create_info.pAllocCb)
+        local_create_info.pAllocCb = &kDefaultAllocCallbacks;
+    create_info = &local_create_info;
+
+    void* instance_mem = create_info->pAllocCb->pfnAlloc(
+        create_info->pAllocCb->pUserData, sizeof(Instance), alignof(Instance),
+        VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
+    if (!instance_mem)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+    Instance* instance = new (instance_mem) Instance(create_info->pAllocCb);
+
+    instance->vtbl_storage = kBottomInstanceFunctions;
+    instance->vtbl_storage.instance = instance;
+
+    // TODO: Insert enabled layers into instance->dispatch_vtbl here.
+
+    // TODO: We'll want to call CreateInstance through the dispatch table
+    // instead of calling the loader's terminator
+    *out_instance = instance;
+    result = CreateInstanceBottom(create_info, out_instance);
+    if (result <= 0) {
+        // For every layer, including the loader top and bottom layers:
+        // - If a call to the next CreateInstance fails, the layer must clean
+        //   up anything it has successfully done so far, and propagate the
+        //   error upwards.
+        // - If a layer successfully calls the next layer's CreateInstance, and
+        //   afterwards must fail for some reason, it must call the next layer's
+        //   DestroyInstance before returning.
+        // - The layer must not call the next layer's DestroyInstance if that
+        //   layer's CreateInstance wasn't called, or returned failure.
+
+        // On failure, CreateInstanceBottom frees the instance struct, so it's
+        // already gone at this point. Nothing to do.
+    }
+
+    return result;
+}
+
+PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* name) {
+    if (!instance)
+        return GetGlobalInstanceProcAddr(name);
+    // For special-case functions we always return the loader entry
+    if (strcmp(name, "vkGetInstanceProcAddr") == 0 ||
+        strcmp(name, "vkGetDeviceProcAddr") == 0) {
+        return GetGlobalInstanceProcAddr(name);
+    }
+    return GetSpecificInstanceProcAddr(instance->vtbl, name);
+}
+
+PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* name) {
+    if (!device)
+        return GetGlobalDeviceProcAddr(name);
+    // For special-case functions we always return the loader entry
+    if (strcmp(name, "vkGetDeviceQueue") == 0 ||
+        strcmp(name, "vkDestroyDevice") == 0) {
+        return GetGlobalDeviceProcAddr(name);
+    }
+    return GetSpecificDeviceProcAddr(GetVtbl(device), name);
+}
+
+VkResult GetDeviceQueue(VkDevice drv_device,
+                        uint32_t family,
+                        uint32_t index,
+                        VkQueue* out_queue) {
+    VkResult result;
+    VkQueue queue;
+    const DeviceVtbl* vtbl = GetVtbl(drv_device);
+    result = vtbl->GetDeviceQueue(drv_device, family, index, &queue);
+    if (result != VK_SUCCESS)
+        return result;
+    hwvulkan_dispatch_t* dispatch =
+        reinterpret_cast<hwvulkan_dispatch_t*>(queue);
+    if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC && dispatch->vtbl != &vtbl) {
+        ALOGE("invalid VkQueue dispatch magic: 0x%" PRIxPTR, dispatch->magic);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+    dispatch->vtbl = vtbl;
+    *out_queue = queue;
+    return VK_SUCCESS;
+}
+
+VkResult DestroyDevice(VkDevice drv_device) {
+    const DeviceVtbl* vtbl = GetVtbl(drv_device);
+    Device* device = static_cast<Device*>(vtbl->device);
+    vtbl->DestroyDevice(drv_device);
+    DestroyDevice(device);
+    return VK_SUCCESS;
+}
+
+}  // namespace vulkan