diff options
author | 2016-04-11 13:51:38 -0700 | |
---|---|---|
committer | 2016-04-11 15:51:57 -0700 | |
commit | b7c4e3b7c97c952ba26f061d74d2038e6c94e689 (patch) | |
tree | fdb4b94bda45b6243580ffec972dbe179e212abc | |
parent | 2b97c063bae8446f2b539e663590399ff0251de8 (diff) |
libvulkan: Use a stub HAL when no real Vulkan HAL is present
This stub HAL enumerates zero VkPhysicalDevices. This allows a
VkInstane to be created and queried for physical devices successfully
even on devices without a Vulkan driver. Handling this with a stub HAL
avoids the need for NULL HAL and NULL driver function pointer checks
in many places throughout the loader, which would be more error-prone.
Fixes bug: 28100673
Change-Id: I76bea975929a85eda354730d6c815567b412b160
-rw-r--r-- | vulkan/libvulkan/Android.mk | 1 | ||||
-rw-r--r-- | vulkan/libvulkan/driver.cpp | 22 | ||||
-rw-r--r-- | vulkan/libvulkan/stubhal.cpp | 134 | ||||
-rw-r--r-- | vulkan/libvulkan/stubhal.h | 30 |
4 files changed, 178 insertions, 9 deletions
diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk index 0979471161..7830c26359 100644 --- a/vulkan/libvulkan/Android.mk +++ b/vulkan/libvulkan/Android.mk @@ -45,6 +45,7 @@ LOCAL_SRC_FILES := \ driver.cpp \ driver_gen.cpp \ layers_extensions.cpp \ + stubhal.cpp \ swapchain.cpp \ vulkan_loader_data.cpp LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 9dbdcc21a6..72b09813ab 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -23,6 +23,7 @@ #include <sys/prctl.h> #include "driver.h" +#include "stubhal.h" // #define ENABLE_ALLOC_CALLSTACKS 1 #if ENABLE_ALLOC_CALLSTACKS @@ -47,7 +48,7 @@ namespace { class CreateInfoWrapper { public: - CreateInfoWrapper(hwvulkan_device_t* hw_dev, + CreateInfoWrapper(const hwvulkan_device_t* hw_dev, const VkInstanceCreateInfo& create_info, const VkAllocationCallbacks& allocator); CreateInfoWrapper(VkPhysicalDevice physical_dev, @@ -87,7 +88,7 @@ class CreateInfoWrapper { const VkAllocationCallbacks& allocator_; union { - hwvulkan_device_t* hw_dev_; + const hwvulkan_device_t* hw_dev_; VkPhysicalDevice physical_dev_; }; @@ -102,7 +103,7 @@ class CreateInfoWrapper { std::bitset<ProcHook::EXTENSION_COUNT> hal_extensions_; }; -CreateInfoWrapper::CreateInfoWrapper(hwvulkan_device_t* hw_dev, +CreateInfoWrapper::CreateInfoWrapper(const hwvulkan_device_t* hw_dev, const VkInstanceCreateInfo& create_info, const VkAllocationCallbacks& allocator) : is_instance_(true), @@ -340,7 +341,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { } } -hwvulkan_device_t* g_hwdevice = nullptr; +const hwvulkan_device_t* g_hwdevice = nullptr; VKAPI_ATTR void* DefaultAllocate(void*, size_t size, @@ -428,15 +429,17 @@ bool Debuggable() { } bool OpenHAL() { - if (g_hwdevice) - return true; + ALOG_ASSERT(!g_hwdevice, "OpenHAL called more than once"); + + // Use a stub device unless we successfully open a real HAL device. + g_hwdevice = &stubhal::kDevice; const hwvulkan_module_t* module; int 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 false; + ALOGV("no Vulkan HAL present, using stub HAL"); + return true; } hwvulkan_device_t* device; @@ -444,7 +447,8 @@ bool OpenHAL() { module->common.methods->open(&module->common, HWVULKAN_DEVICE_0, reinterpret_cast<hw_device_t**>(&device)); if (result != 0) { - ALOGE("failed to open vulkan driver: %s (%d)", strerror(-result), + // Any device with a Vulkan HAL should be able to open the device. + ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result), result); return false; } diff --git a/vulkan/libvulkan/stubhal.cpp b/vulkan/libvulkan/stubhal.cpp new file mode 100644 index 0000000000..89fcebbd27 --- /dev/null +++ b/vulkan/libvulkan/stubhal.cpp @@ -0,0 +1,134 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* NOTE: + * This stub HAL is only used internally by the loader when a real HAL + * implementation is not present, in order to avoid needing "null HAL" checks + * throughout the loader. It does not enumerate any physical devices, and is + * only as conformant to the Vulkan and Android HAL interfaces as the loader + * needs it to be. Do not use it as an example of a correct implementation; the + * code in ../null_driver is better for that. + */ + +#undef LOG_TAG +#define LOG_TAG "vkstub" + +#include <array> +#include <bitset> +#include <mutex> +#include <hardware/hwvulkan.h> +#include <log/log.h> +#include "stubhal.h" + +namespace vulkan { +namespace stubhal { + +namespace { + +const size_t kMaxInstances = 32; +static std::mutex g_instance_mutex; +static std::bitset<kMaxInstances> g_instance_used(false); +static std::array<hwvulkan_dispatch_t, kMaxInstances> g_instances; + +[[noreturn]] void NoOp() { + LOG_ALWAYS_FATAL("invalid stub function called"); +} + +VKAPI_ATTR VkResult +EnumerateInstanceExtensionProperties(const char* /*layer_name*/, + uint32_t* count, + VkExtensionProperties* /*properties*/) { + *count = 0; + return VK_SUCCESS; +} + +VKAPI_ATTR VkResult +EnumerateInstanceLayerProperties(uint32_t* count, + VkLayerProperties* /*properties*/) { + *count = 0; + return VK_SUCCESS; +} + +VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* /*create_info*/, + const VkAllocationCallbacks* /*allocator*/, + VkInstance* instance) { + std::lock_guard<std::mutex> lock(g_instance_mutex); + for (size_t i = 0; i < kMaxInstances; i++) { + if (!g_instance_used[i]) { + g_instance_used[i] = true; + g_instances[i].magic = HWVULKAN_DISPATCH_MAGIC; + *instance = reinterpret_cast<VkInstance>(&g_instances[i]); + return VK_SUCCESS; + } + } + ALOGE("no more instances available (max=%zu)", kMaxInstances); + return VK_ERROR_INITIALIZATION_FAILED; +} + +VKAPI_ATTR void DestroyInstance(VkInstance instance, + const VkAllocationCallbacks* /*allocator*/) { + std::lock_guard<std::mutex> lock(g_instance_mutex); + ssize_t idx = + reinterpret_cast<hwvulkan_dispatch_t*>(instance) - &g_instances[0]; + ALOG_ASSERT(idx >= 0 && idx < g_instance_used.size(), + "DestroyInstance: invalid instance handle"); + g_instance_used[static_cast<size_t>(idx)] = false; +} + +VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance /*instance*/, + uint32_t* count, + VkPhysicalDevice* /*gpus*/) { + *count = 0; + return VK_SUCCESS; +} + +VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance /*instance*/, + const char* name) { + if (strcmp(name, "vkCreateInstance") == 0) + return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance); + if (strcmp(name, "vkDestroyInstance") == 0) + return reinterpret_cast<PFN_vkVoidFunction>(DestroyInstance); + if (strcmp(name, "vkEnumerateInstanceExtensionProperties") == 0) + return reinterpret_cast<PFN_vkVoidFunction>( + EnumerateInstanceExtensionProperties); + if (strcmp(name, "vkEnumeratePhysicalDevices") == 0) + return reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDevices); + if (strcmp(name, "vkGetInstanceProcAddr") == 0) + return reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr); + + // None of the other Vulkan functions should ever be called, as they all + // take a VkPhysicalDevice or other object obtained from a physical device. + return reinterpret_cast<PFN_vkVoidFunction>(NoOp); +} + +} // anonymous namespace + +const hwvulkan_device_t kDevice = { + .common = + { + .tag = HARDWARE_DEVICE_TAG, + .version = HWVULKAN_DEVICE_API_VERSION_0_1, + .module = nullptr, + .close = nullptr, + }, + .EnumerateInstanceExtensionProperties = + EnumerateInstanceExtensionProperties, + .CreateInstance = CreateInstance, + .GetInstanceProcAddr = GetInstanceProcAddr, +}; + +} // namespace stubhal +} // namespace vulkan diff --git a/vulkan/libvulkan/stubhal.h b/vulkan/libvulkan/stubhal.h new file mode 100644 index 0000000000..9ba7d04067 --- /dev/null +++ b/vulkan/libvulkan/stubhal.h @@ -0,0 +1,30 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBVULKAN_STUBHAL_H +#define LIBVULKAN_STUBHAL_H 1 + +struct hwvulkan_device_t; + +namespace vulkan { +namespace stubhal { + +extern const hwvulkan_device_t kDevice; + +} // namespace stubhal +} // namespace vulkan + +#endif // LIBVULKAN_STUBHAL_H |