vulkan: Driver device extension enumeration and filtering
- Return VK_ERROR_EXTENSION_NOT_PRESENT if a requested device
extension is not supported by the loader, driver, or any enabled
device layer.
- Filter out device extensions not supported by the driver when
creating the driver device.
- Enumerate device extensions supported by the driver or loader.
Change-Id: I538e37bc74cc7f0eb27b1211b9324fb3b8a06e14
(cherry picked from commit 35873021f4f79ded0f584e433076c2675c6aed69)
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index f2fbf31..3e7fbec 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -28,6 +28,12 @@
using namespace vulkan;
+// TODO(jessehall): The whole way we deal with extensions is pretty hokey, and
+// not a good long-term solution. Having a hard-coded enum of extensions is
+// bad, of course. Representing sets of extensions (requested, supported, etc.)
+// as a bitset isn't necessarily bad, if the mapping from extension to bit were
+// dynamic. Need to rethink this completely when there's a little more time.
+
// TODO(jessehall): This file currently builds up global data structures as it
// loads, and never cleans them up. This means we're doing heap allocations
// without going through an app-provided allocator, but worse, we'll leak those
@@ -184,7 +190,7 @@
}
g_instance_layers.push_back(layer);
- ALOGV("added instance layer '%s'", props.layerName);
+ ALOGV(" added instance layer '%s'", props.layerName);
}
for (size_t i = 0; i < num_device_layers; i++) {
const VkLayerProperties& props = properties[num_instance_layers + i];
@@ -226,7 +232,7 @@
}
g_device_layers.push_back(layer);
- ALOGV("added device layer '%s'", props.layerName);
+ ALOGV(" added device layer '%s'", props.layerName);
}
dlclose(dlhandle);
@@ -396,6 +402,13 @@
: nullptr;
}
+bool LayerRef::SupportsExtension(const char* name) const {
+ return std::find_if(layer_->extensions.cbegin(), layer_->extensions.cend(),
+ [=](const VkExtensionProperties& ext) {
+ return strcmp(ext.extensionName, name) == 0;
+ }) != layer_->extensions.cend();
+}
+
InstanceExtension InstanceExtensionFromName(const char* name) {
if (strcmp(name, VK_KHR_SURFACE_EXTENSION_NAME) == 0)
return kKHR_surface;
@@ -406,4 +419,12 @@
return kInstanceExtensionCount;
}
+DeviceExtension DeviceExtensionFromName(const char* name) {
+ if (strcmp(name, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0)
+ return kKHR_swapchain;
+ if (strcmp(name, VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) == 0)
+ return kANDROID_native_buffer;
+ return kDeviceExtensionCount;
+}
+
} // namespace vulkan
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index 985b7e4..5abd3c5 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -268,6 +268,7 @@
const VkAllocationCallbacks* alloc;
uint32_t num_physical_devices;
VkPhysicalDevice physical_devices[kMaxPhysicalDevices];
+ DeviceExtensionSet physical_device_driver_extensions[kMaxPhysicalDevices];
Vector<LayerRef> active_layers;
VkDebugReportCallbackEXT message;
@@ -275,7 +276,6 @@
struct {
VkInstance instance;
- InstanceExtensionSet supported_extensions;
DriverDispatchTable dispatch;
uint32_t num_physical_devices;
} drv; // may eventually be an array
@@ -582,6 +582,9 @@
DestroyInstance_Bottom(instance.handle, allocator);
return VK_ERROR_INITIALIZATION_FAILED;
}
+
+ Vector<VkExtensionProperties> extensions(
+ Vector<VkExtensionProperties>::allocator_type(instance.alloc));
for (uint32_t i = 0; i < num_physical_devices; i++) {
hwvulkan_dispatch_t* pdev_dispatch =
reinterpret_cast<hwvulkan_dispatch_t*>(
@@ -593,10 +596,41 @@
return VK_ERROR_INITIALIZATION_FAILED;
}
pdev_dispatch->vtbl = instance.dispatch_ptr;
+
+ uint32_t count;
+ if ((result = instance.drv.dispatch.EnumerateDeviceExtensionProperties(
+ instance.physical_devices[i], nullptr, &count, nullptr)) !=
+ VK_SUCCESS) {
+ ALOGW("driver EnumerateDeviceExtensionProperties(%u) failed: %d", i,
+ result);
+ continue;
+ }
+ extensions.resize(count);
+ if ((result = instance.drv.dispatch.EnumerateDeviceExtensionProperties(
+ instance.physical_devices[i], nullptr, &count,
+ extensions.data())) != VK_SUCCESS) {
+ ALOGW("driver EnumerateDeviceExtensionProperties(%u) failed: %d", i,
+ result);
+ continue;
+ }
+ ALOGV_IF(count > 0, "driver gpu[%u] supports extensions:", i);
+ for (const auto& extension : extensions) {
+ ALOGV(" %s (v%u)", extension.extensionName, extension.specVersion);
+ DeviceExtension id =
+ DeviceExtensionFromName(extension.extensionName);
+ if (id == kDeviceExtensionCount) {
+ ALOGW("driver gpu[%u] extension '%s' unknown to loader", i,
+ extension.extensionName);
+ } else {
+ instance.physical_device_driver_extensions[i].set(id);
+ }
+ }
+ // Ignore driver attempts to support loader extensions
+ instance.physical_device_driver_extensions[i].reset(kKHR_swapchain);
}
instance.drv.num_physical_devices = num_physical_devices;
-
instance.num_physical_devices = instance.drv.num_physical_devices;
+
return VK_SUCCESS;
}
@@ -686,7 +720,7 @@
VKAPI_ATTR
VkResult EnumerateDeviceExtensionProperties_Bottom(
- VkPhysicalDevice /*pdev*/,
+ VkPhysicalDevice gpu,
const char* layer_name,
uint32_t* properties_count,
VkExtensionProperties* properties) {
@@ -695,7 +729,26 @@
if (layer_name) {
GetDeviceLayerExtensions(layer_name, &extensions, &num_extensions);
} else {
- // TODO(jessehall)
+ Instance& instance = GetDispatchParent(gpu);
+ size_t gpu_idx = 0;
+ while (instance.physical_devices[gpu_idx] != gpu)
+ gpu_idx++;
+ const DeviceExtensionSet driver_extensions =
+ instance.physical_device_driver_extensions[gpu_idx];
+
+ // We only support VK_KHR_swapchain if the GPU supports
+ // VK_ANDROID_native_buffer
+ VkExtensionProperties* available = static_cast<VkExtensionProperties*>(
+ alloca(kDeviceExtensionCount * sizeof(VkExtensionProperties)));
+ if (driver_extensions[kANDROID_native_buffer]) {
+ available[num_extensions++] = VkExtensionProperties{
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_SPEC_VERSION};
+ }
+
+ // TODO(jessehall): We need to also enumerate extensions supported by
+ // implicitly-enabled layers. Currently we don't have that list of
+ // layers until instance creation.
+ extensions = available;
}
if (!properties || *properties_count > num_extensions)
@@ -717,11 +770,11 @@
}
VKAPI_ATTR
-VkResult CreateDevice_Bottom(VkPhysicalDevice pdev,
+VkResult CreateDevice_Bottom(VkPhysicalDevice gpu,
const VkDeviceCreateInfo* create_info,
const VkAllocationCallbacks* allocator,
VkDevice* device_out) {
- Instance& instance = GetDispatchParent(pdev);
+ Instance& instance = GetDispatchParent(gpu);
VkResult result;
if (!allocator) {
@@ -744,7 +797,41 @@
return result;
}
- const char* kAndroidNativeBufferExtensionName = "VK_ANDROID_native_buffer";
+ size_t gpu_idx = 0;
+ while (instance.physical_devices[gpu_idx] != gpu)
+ gpu_idx++;
+
+ uint32_t num_driver_extensions = 0;
+ const char** driver_extensions = static_cast<const char**>(
+ alloca(create_info->enabledExtensionCount * sizeof(const char*)));
+ for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
+ const char* name = create_info->ppEnabledExtensionNames[i];
+
+ DeviceExtension id = DeviceExtensionFromName(name);
+ if (id < kDeviceExtensionCount &&
+ (instance.physical_device_driver_extensions[gpu_idx][id] ||
+ id == kKHR_swapchain)) {
+ if (id == kKHR_swapchain)
+ name = VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
+ driver_extensions[num_driver_extensions++] = name;
+ continue;
+ }
+
+ bool supported = false;
+ for (const auto& layer : device->active_layers) {
+ if (layer.SupportsExtension(name))
+ supported = true;
+ }
+ if (!supported) {
+ ALOGE(
+ "requested device extension '%s' not supported by driver or "
+ "any active layers",
+ name);
+ DestroyDevice(device);
+ return VK_ERROR_EXTENSION_NOT_PRESENT;
+ }
+ }
+
VkDeviceCreateInfo driver_create_info = *create_info;
driver_create_info.enabledLayerCount = 0;
driver_create_info.ppEnabledLayerNames = nullptr;
@@ -753,12 +840,12 @@
// supported by the driver here. Also, add the VK_ANDROID_native_buffer
// extension to the list iff the VK_KHR_swapchain extension was requested,
// instead of adding it unconditionally like we do now.
- driver_create_info.enabledExtensionCount = 1;
- driver_create_info.ppEnabledExtensionNames = &kAndroidNativeBufferExtensionName;
+ driver_create_info.enabledExtensionCount = num_driver_extensions;
+ driver_create_info.ppEnabledExtensionNames = driver_extensions;
VkDevice drv_device;
- result = instance.drv.dispatch.CreateDevice(pdev, &driver_create_info, allocator,
- &drv_device);
+ result = instance.drv.dispatch.CreateDevice(gpu, &driver_create_info,
+ allocator, &drv_device);
if (result != VK_SUCCESS) {
DestroyDevice(device);
return result;
@@ -817,7 +904,7 @@
// therefore which functions to return procaddrs for.
PFN_vkCreateDevice create_device = reinterpret_cast<PFN_vkCreateDevice>(
next_get_proc_addr(drv_device, "vkCreateDevice"));
- create_device(pdev, create_info, allocator, &drv_device);
+ create_device(gpu, create_info, allocator, &drv_device);
if (!LoadDeviceDispatchTable(static_cast<VkDevice>(base_object),
next_get_proc_addr, device->dispatch)) {
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
index 375396e..3e2d1c4 100644
--- a/vulkan/libvulkan/loader.h
+++ b/vulkan/libvulkan/loader.h
@@ -31,6 +31,13 @@
};
typedef std::bitset<kInstanceExtensionCount> InstanceExtensionSet;
+enum DeviceExtension {
+ kKHR_swapchain,
+ kANDROID_native_buffer,
+ kDeviceExtensionCount
+};
+typedef std::bitset<kDeviceExtensionCount> DeviceExtensionSet;
+
inline const InstanceDispatchTable& GetDispatchTable(VkInstance instance) {
return **reinterpret_cast<InstanceDispatchTable**>(instance);
}
@@ -149,6 +156,8 @@
PFN_vkGetInstanceProcAddr GetGetInstanceProcAddr() const;
PFN_vkGetDeviceProcAddr GetGetDeviceProcAddr() const;
+ bool SupportsExtension(const char* name) const;
+
private:
Layer* layer_;
};
@@ -166,6 +175,7 @@
LayerRef GetDeviceLayerRef(const char* name);
InstanceExtension InstanceExtensionFromName(const char* name);
+DeviceExtension DeviceExtensionFromName(const char* name);
} // namespace vulkan