| // Copyright 2019 Google LLC |
| // |
| // 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 |
| // |
| // https://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. |
| |
| #include "iree/hal/vulkan/api.h" |
| |
| #include "iree/base/api.h" |
| #include "iree/base/tracing.h" |
| #include "iree/hal/vulkan/dynamic_symbols.h" |
| #include "iree/hal/vulkan/extensibility_util.h" |
| #include "iree/hal/vulkan/vulkan_device.h" |
| #include "iree/hal/vulkan/vulkan_driver.h" |
| |
| namespace iree { |
| namespace hal { |
| namespace vulkan { |
| |
| //===----------------------------------------------------------------------===// |
| // iree::hal::vulkan::DynamicSymbols |
| //===----------------------------------------------------------------------===// |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_vulkan_syms_create( |
| void* vkGetInstanceProcAddr_fn, iree_hal_vulkan_syms_t** out_syms) { |
| IREE_TRACE_SCOPE0("iree_hal_vulkan_syms_create"); |
| IREE_ASSERT_ARGUMENT(out_syms); |
| *out_syms = nullptr; |
| |
| IREE_ASSIGN_OR_RETURN( |
| auto syms, DynamicSymbols::Create([&vkGetInstanceProcAddr_fn]( |
| const char* function_name) { |
| // Only resolve vkGetInstanceProcAddr, rely on syms->LoadFromInstance() |
| // and/or syms->LoadFromDevice() for further loading. |
| std::string fn = "vkGetInstanceProcAddr"; |
| if (strncmp(function_name, fn.data(), fn.size()) == 0) { |
| return reinterpret_cast<PFN_vkVoidFunction>(vkGetInstanceProcAddr_fn); |
| } |
| return reinterpret_cast<PFN_vkVoidFunction>(NULL); |
| })); |
| |
| *out_syms = reinterpret_cast<iree_hal_vulkan_syms_t*>(syms.release()); |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL |
| iree_hal_vulkan_syms_create_from_system_loader( |
| iree_hal_vulkan_syms_t** out_syms) { |
| IREE_TRACE_SCOPE0("iree_hal_vulkan_syms_create_from_system_loader"); |
| IREE_ASSERT_ARGUMENT(out_syms); |
| *out_syms = nullptr; |
| |
| IREE_ASSIGN_OR_RETURN(auto syms, DynamicSymbols::CreateFromSystemLoader()); |
| *out_syms = reinterpret_cast<iree_hal_vulkan_syms_t*>(syms.release()); |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t |
| iree_hal_vulkan_syms_release(iree_hal_vulkan_syms_t* syms) { |
| IREE_TRACE_SCOPE0("iree_hal_vulkan_syms_release"); |
| IREE_ASSERT_ARGUMENT(syms); |
| auto* handle = reinterpret_cast<DynamicSymbols*>(syms); |
| handle->ReleaseReference(); |
| return iree_ok_status(); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // iree::hal::vulkan Extensibility Util |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| ExtensibilitySpec GetInstanceExtensibilitySpec( |
| const iree_hal_vulkan_features_t& features) { |
| ExtensibilitySpec spec; |
| |
| // Multiple extensions depend on VK_KHR_get_physical_device_properties2. |
| // This extension was deprecated in Vulkan 1.1 as its functionality was |
| // promoted to core, so we list it as optional even though we require it. |
| spec.optional_extensions.push_back( |
| VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| |
| if (features & IREE_HAL_VULKAN_ENABLE_VALIDATION_LAYERS) { |
| spec.optional_layers.push_back("VK_LAYER_KHRONOS_standard_validation"); |
| } |
| |
| if (features & IREE_HAL_VULKAN_ENABLE_DEBUG_UTILS) { |
| spec.optional_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); |
| } |
| |
| // Polyfill layer - enable if present. |
| spec.optional_layers.push_back("VK_LAYER_KHRONOS_timeline_semaphore"); |
| |
| return spec; |
| } |
| |
| ExtensibilitySpec GetDeviceExtensibilitySpec( |
| const iree_hal_vulkan_features_t& features) { |
| ExtensibilitySpec spec; |
| |
| // REQUIRED: these are required extensions that must be present for IREE to |
| // work (such as those relied upon by SPIR-V kernels, etc). |
| spec.required_extensions.push_back( |
| VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME); |
| // Timeline semaphore support is required. |
| spec.required_extensions.push_back(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME); |
| |
| if (features & IREE_HAL_VULKAN_ENABLE_PUSH_DESCRIPTORS) { |
| spec.optional_extensions.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); |
| } |
| |
| return spec; |
| } |
| |
| } // namespace |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_vulkan_get_extensions( |
| iree_hal_vulkan_extensibility_set_t extensibility_set, |
| iree_hal_vulkan_features_t features, iree_host_size_t extensions_capacity, |
| const char** out_extensions, iree_host_size_t* out_extensions_count) { |
| IREE_ASSERT_ARGUMENT(out_extensions_count); |
| *out_extensions_count = 0; |
| |
| bool is_instance = extensibility_set & IREE_HAL_VULKAN_INSTANCE_BIT; |
| bool is_required = extensibility_set & IREE_HAL_VULKAN_REQUIRED_BIT; |
| |
| ExtensibilitySpec spec = is_instance ? GetInstanceExtensibilitySpec(features) |
| : GetDeviceExtensibilitySpec(features); |
| *out_extensions_count = is_required ? spec.required_extensions.size() |
| : spec.optional_extensions.size(); |
| |
| // Return early if only querying number of extensions in this configuration. |
| if (!out_extensions) { |
| return iree_ok_status(); |
| } |
| |
| if (extensions_capacity < *out_extensions_count) { |
| // Not an error; just a size query. |
| return iree_status_from_code(IREE_STATUS_OUT_OF_RANGE); |
| } |
| |
| const std::vector<const char*>& extensions = |
| is_required ? spec.required_extensions : spec.optional_extensions; |
| for (int i = 0; i < extensions.size(); ++i) { |
| out_extensions[i] = extensions[i]; |
| } |
| |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_vulkan_get_layers( |
| iree_hal_vulkan_extensibility_set_t extensibility_set, |
| iree_hal_vulkan_features_t features, iree_host_size_t layers_capacity, |
| const char** out_layers, iree_host_size_t* out_layers_count) { |
| IREE_ASSERT_ARGUMENT(out_layers_count); |
| *out_layers_count = 0; |
| |
| // Device layers are deprecated and unsupported here. |
| if (!(extensibility_set & IREE_HAL_VULKAN_INSTANCE_BIT)) { |
| return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, |
| "device layers are deprecated in Vulkan"); |
| } |
| |
| bool is_required = extensibility_set & IREE_HAL_VULKAN_REQUIRED_BIT; |
| |
| ExtensibilitySpec spec = GetInstanceExtensibilitySpec(features); |
| *out_layers_count = |
| is_required ? spec.required_layers.size() : spec.optional_layers.size(); |
| |
| // Return early if only querying number of layers in this configuration. |
| if (!out_layers) { |
| return iree_ok_status(); |
| } |
| |
| if (layers_capacity < *out_layers_count) { |
| // Not an error; just a size query. |
| return iree_status_from_code(IREE_STATUS_OUT_OF_RANGE); |
| } |
| |
| const std::vector<const char*>& layers = |
| is_required ? spec.required_layers : spec.optional_layers; |
| for (int i = 0; i < layers.size(); ++i) { |
| out_layers[i] = layers[i]; |
| } |
| |
| return iree_ok_status(); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // iree::hal::vulkan::VulkanDriver |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| VulkanDriver::Options ConvertDriverOptions( |
| iree_hal_vulkan_driver_options_t options) { |
| VulkanDriver::Options driver_options; |
| driver_options.api_version = options.api_version; |
| driver_options.instance_extensibility = |
| GetInstanceExtensibilitySpec(options.features); |
| driver_options.device_options.extensibility_spec = |
| GetDeviceExtensibilitySpec(options.features); |
| return driver_options; |
| } |
| |
| } // namespace |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_vulkan_driver_create( |
| iree_hal_vulkan_driver_options_t options, iree_hal_vulkan_syms_t* syms, |
| iree_hal_driver_t** out_driver) { |
| IREE_TRACE_SCOPE0("iree_hal_vulkan_driver_create"); |
| IREE_ASSERT_ARGUMENT(syms); |
| IREE_ASSERT_ARGUMENT(out_driver); |
| *out_driver = nullptr; |
| |
| IREE_ASSIGN_OR_RETURN( |
| auto driver, |
| VulkanDriver::Create(ConvertDriverOptions(options), |
| add_ref(reinterpret_cast<DynamicSymbols*>(syms)))); |
| *out_driver = reinterpret_cast<iree_hal_driver_t*>(driver.release()); |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL |
| iree_hal_vulkan_driver_create_using_instance( |
| iree_hal_vulkan_driver_options_t options, iree_hal_vulkan_syms_t* syms, |
| VkInstance instance, iree_hal_driver_t** out_driver) { |
| IREE_TRACE_SCOPE0("iree_hal_vulkan_driver_create_using_instance"); |
| IREE_ASSERT_ARGUMENT(syms); |
| IREE_ASSERT_ARGUMENT(instance); |
| IREE_ASSERT_ARGUMENT(out_driver); |
| *out_driver = nullptr; |
| |
| IREE_ASSIGN_OR_RETURN( |
| auto driver, |
| VulkanDriver::CreateUsingInstance( |
| ConvertDriverOptions(options), |
| add_ref(reinterpret_cast<DynamicSymbols*>(syms)), instance)); |
| *out_driver = reinterpret_cast<iree_hal_driver_t*>(driver.release()); |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL |
| iree_hal_vulkan_driver_create_default_device(iree_hal_driver_t* driver, |
| iree_hal_device_t** out_device) { |
| IREE_TRACE_SCOPE0("iree_hal_vulkan_driver_create_default_device"); |
| IREE_ASSERT_ARGUMENT(driver); |
| IREE_ASSERT_ARGUMENT(out_device); |
| *out_device = nullptr; |
| |
| auto* handle = reinterpret_cast<VulkanDriver*>(driver); |
| |
| IREE_LOG(INFO) << "Enumerating available Vulkan devices..."; |
| IREE_ASSIGN_OR_RETURN(auto available_devices, |
| handle->EnumerateAvailableDevices()); |
| for (const auto& device_info : available_devices) { |
| IREE_LOG(INFO) << " Device: " << device_info.name(); |
| } |
| IREE_LOG(INFO) << "Creating default device..."; |
| IREE_ASSIGN_OR_RETURN(auto device, handle->CreateDefaultDevice()); |
| IREE_LOG(INFO) << "Successfully created device '" << device->info().name() |
| << "'"; |
| |
| *out_device = reinterpret_cast<iree_hal_device_t*>(device.release()); |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_vulkan_driver_wrap_device( |
| iree_hal_driver_t* driver, VkPhysicalDevice physical_device, |
| VkDevice logical_device, iree_hal_vulkan_queue_set_t compute_queue_set, |
| iree_hal_vulkan_queue_set_t transfer_queue_set, |
| iree_hal_device_t** out_device) { |
| IREE_TRACE_SCOPE0("iree_hal_vulkan_driver_create_device"); |
| IREE_ASSERT_ARGUMENT(driver); |
| IREE_ASSERT_ARGUMENT(physical_device); |
| IREE_ASSERT_ARGUMENT(logical_device); |
| IREE_ASSERT_ARGUMENT(out_device); |
| *out_device = nullptr; |
| |
| auto* handle = reinterpret_cast<VulkanDriver*>(driver); |
| |
| IREE_LOG(INFO) << "Creating VulkanDevice..."; |
| QueueSet compute_qs; |
| compute_qs.queue_family_index = compute_queue_set.queue_family_index; |
| compute_qs.queue_indices = compute_queue_set.queue_indices; |
| QueueSet transfer_qs; |
| transfer_qs.queue_family_index = transfer_queue_set.queue_family_index; |
| transfer_qs.queue_indices = transfer_queue_set.queue_indices; |
| IREE_ASSIGN_OR_RETURN(auto device, |
| handle->WrapDevice(physical_device, logical_device, |
| compute_qs, transfer_qs)); |
| IREE_LOG(INFO) << "Successfully created device '" << device->info().name() |
| << "'"; |
| |
| *out_device = reinterpret_cast<iree_hal_device_t*>(device.release()); |
| |
| return iree_ok_status(); |
| } |
| |
| } // namespace vulkan |
| } // namespace hal |
| } // namespace iree |