blob: 2432d5563c6be7eda0693ef3561c87dd4e82a332 [file]
// Copyright 2026 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/hal/drivers/vulkan/logical_device.h"
#include <string.h>
#include "iree/base/internal/arena.h"
#include "iree/base/target_platform.h"
#if defined(IREE_PLATFORM_WINDOWS)
#include <windows.h>
#endif // defined(IREE_PLATFORM_WINDOWS)
#include "iree/async/frontier.h"
#include "iree/async/frontier_tracker.h"
#include "iree/async/util/proactor_pool.h"
#include "iree/hal/drivers/vulkan/allocator.h"
#include "iree/hal/drivers/vulkan/builtins.h"
#include "iree/hal/drivers/vulkan/command_buffer.h"
#include "iree/hal/drivers/vulkan/debug_utils.h"
#include "iree/hal/drivers/vulkan/device_options.h"
#include "iree/hal/drivers/vulkan/device_plan.h"
#include "iree/hal/drivers/vulkan/executable.h"
#include "iree/hal/drivers/vulkan/executable_cache.h"
#include "iree/hal/drivers/vulkan/physical_device.h"
#include "iree/hal/drivers/vulkan/physical_device_selection.h"
#include "iree/hal/drivers/vulkan/queue.h"
#include "iree/hal/drivers/vulkan/semaphore.h"
#include "iree/hal/drivers/vulkan/syms.h"
#include "iree/hal/local/profile.h"
#include "iree/hal/utils/file_registry.h"
//===----------------------------------------------------------------------===//
// Resolved queue handles
//===----------------------------------------------------------------------===//
#define IREE_HAL_VULKAN_COMMAND_BUFFER_BLOCK_SIZE (64 * 1024)
typedef struct iree_hal_vulkan_resolved_queue_t {
// Queue identity and queue-family capability facts selected by the plan.
iree_hal_vulkan_queue_selection_t selection;
// Vulkan queue handle borrowed from the logical device.
VkQueue handle;
} iree_hal_vulkan_resolved_queue_t;
//===----------------------------------------------------------------------===//
// iree_hal_vulkan_logical_device_t
//===----------------------------------------------------------------------===//
struct iree_hal_vulkan_logical_device_t {
// HAL resource header.
iree_hal_resource_t resource;
// Host allocator used for logical-device-owned host allocations.
iree_allocator_t host_allocator;
// Stable device identifier stored inline after this struct.
iree_string_view_t identifier;
// Retained Vulkan loader keeping resolved entry-point code live.
iree_hal_vulkan_libvulkan_t libvulkan;
// Proactor pool retained from create_params for async host waits.
iree_async_proactor_pool_t* proactor_pool;
// Proactor borrowed from the pool for device-local async operations.
iree_async_proactor_t* proactor;
// Driver-owned Vulkan instance and instance dispatch table.
iree_hal_vulkan_instance_t instance;
// True when destruction should call vkDestroyInstance.
bool owns_instance;
// Immutable physical-device inventory used for capability decisions.
iree_hal_vulkan_physical_device_snapshot_t physical_device;
// Vulkan logical device handle.
VkDevice logical_device;
// True when destruction should call vkDestroyDevice.
bool owns_logical_device;
// Device-level Vulkan dispatch table.
iree_hal_vulkan_device_syms_t syms;
// Resolved VK_EXT_debug_utils capabilities.
iree_hal_vulkan_debug_utils_t debug_utils;
// HAL feature bits enabled on the logical device.
iree_hal_vulkan_features_t enabled_features;
// Executable dispatch ABI bits enabled on the logical device.
iree_hal_vulkan_dispatch_abis_t enabled_dispatch_abis;
// Host block pool for command-buffer resource sets and future command blocks.
iree_arena_block_pool_t command_buffer_block_pool;
// Recognized Vulkan device extension bits enabled on the logical device.
iree_hal_vulkan_device_extensions_t enabled_extensions;
// Device-owned built-in pipelines used by queue command polyfills.
iree_hal_vulkan_builtins_t builtins;
// Resolved queue topology and initialized queue lanes.
struct {
// Selected compute-capable queue.
iree_hal_vulkan_resolved_queue_t compute;
// Selected transfer-capable queue.
iree_hal_vulkan_resolved_queue_t transfer;
// Internal queue used for sparse memory binding operations.
iree_hal_vulkan_resolved_queue_t sparse_binding;
// Host synchronization objects for borrowed VkQueue handles.
struct {
// Serializes host access to the selected compute queue handle.
iree_slim_mutex_t compute;
// Serializes host access to the selected transfer queue handle.
iree_slim_mutex_t transfer;
// Serializes host access to a distinct sparse-binding queue handle.
iree_slim_mutex_t sparse_binding;
} handle_mutexes;
// Queue lanes initialized from distinct selected queues, including hidden
// internal lanes such as sparse binding.
iree_hal_vulkan_queue_t lanes[IREE_HAL_VULKAN_MAX_QUEUE_LANES];
// Count of initialized entries in lanes.
iree_host_size_t lane_count;
// Queue lane used for compute-capable submissions.
iree_hal_vulkan_queue_t* compute_lane;
// Queue lane used for transfer-capable submissions.
iree_hal_vulkan_queue_t* transfer_lane;
// Internal queue lane used for sparse binding submissions.
iree_hal_vulkan_queue_t* sparse_binding_lane;
// Count of distinct HAL queues exposed through queue affinity.
iree_host_size_t count;
// Mask of valid queue affinity bits for this logical device.
iree_hal_queue_affinity_t affinity_mask;
} queues;
// Maximum cached native BDA replay instances retained per queue lane.
uint32_t max_cached_bda_replay_instances;
// Maximum BDA publication bytes retained by cached replay instances per lane.
uint64_t max_cached_bda_replay_publication_bytes;
// Idle cached native BDA replay instances retained per lane after trim.
uint32_t retained_cached_bda_replay_instances;
// Logical allocator.
iree_hal_allocator_t* device_allocator;
// Active profiling session state.
struct {
// Active HAL-native profile recorder, when profiling is enabled.
iree_hal_local_profile_recorder_t* recorder;
// Host time domain selected for calibrated profiling samples.
VkTimeDomainEXT host_time_domain;
// Next process-local profiling session id.
uint64_t next_session_id;
// Next clock-correlation sample id for the active profiling session.
uint64_t next_clock_correlation_sample_id;
// Next profile submission id shared across queue lanes.
iree_atomic_int64_t next_submission_id;
// Clock alignment state shared with queue lanes while profiling is active.
iree_hal_vulkan_profile_clock_alignment_t clock_alignment;
} profile;
// Optional provider used for creating/configuring collective channels.
iree_hal_channel_provider_t* channel_provider;
// Shared frontier tracker retained after topology assignment.
iree_async_frontier_tracker_t* frontier_tracker;
// Topology-assigned axis registered with the frontier tracker.
iree_async_axis_t axis;
// Topology information if this device is part of a multi-device topology.
iree_hal_device_topology_info_t topology_info;
// + trailing identifier string storage.
};
static const iree_hal_device_vtable_t iree_hal_vulkan_logical_device_vtable;
static iree_hal_vulkan_logical_device_t* iree_hal_vulkan_logical_device_cast(
iree_hal_device_t* base_value) {
IREE_HAL_ASSERT_TYPE(base_value, &iree_hal_vulkan_logical_device_vtable);
return (iree_hal_vulkan_logical_device_t*)base_value;
}
static bool iree_hal_vulkan_logical_device_isa(iree_hal_device_t* base_value) {
return iree_hal_resource_is((const iree_hal_resource_t*)base_value,
&iree_hal_vulkan_logical_device_vtable);
}
static iree_status_t iree_hal_vulkan_unimplemented(
iree_string_view_t operation) {
return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
"Vulkan %.*s is not implemented in the Vulkan HAL",
(int)operation.size, operation.data);
}
// Power-of-two capacity for logical-device memory lifecycle event buffering.
#define IREE_HAL_VULKAN_LOGICAL_DEVICE_PROFILE_MEMORY_EVENT_CAPACITY (64 * 1024)
// Power-of-two capacity for logical-device queue operation event buffering.
#define IREE_HAL_VULKAN_LOGICAL_DEVICE_PROFILE_QUEUE_EVENT_CAPACITY (64 * 1024)
// Power-of-two capacity for logical-device dispatch event buffering.
#define IREE_HAL_VULKAN_LOGICAL_DEVICE_PROFILE_DISPATCH_EVENT_CAPACITY \
(64 * 1024)
// Power-of-two capacity for logical-device queue device event buffering.
#define IREE_HAL_VULKAN_LOGICAL_DEVICE_PROFILE_QUEUE_DEVICE_EVENT_CAPACITY \
(64 * 1024)
static bool iree_hal_vulkan_logical_device_query_pool_epoch(
void* user_data, iree_async_axis_t axis, uint64_t epoch) {
iree_hal_vulkan_logical_device_t* device =
(iree_hal_vulkan_logical_device_t*)user_data;
return device->frontier_tracker && iree_async_frontier_tracker_query_epoch(
device->frontier_tracker, axis, epoch);
}
static uint32_t iree_hal_vulkan_logical_device_profile_count(
iree_host_size_t value) {
return value > UINT32_MAX ? UINT32_MAX : (uint32_t)value;
}
static iree_hal_device_profiling_data_families_t
iree_hal_vulkan_logical_device_lightweight_statistics_data_families(void) {
return IREE_HAL_DEVICE_PROFILING_DATA_QUEUE_EVENTS |
IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_METADATA |
IREE_HAL_DEVICE_PROFILING_DATA_MEMORY_EVENTS;
}
static iree_hal_device_profiling_options_t
iree_hal_vulkan_logical_device_resolve_profiling_options(
const iree_hal_device_profiling_options_t* options) {
iree_hal_device_profiling_options_t resolved_options = *options;
if (resolved_options.data_families == IREE_HAL_DEVICE_PROFILING_DATA_NONE &&
iree_hal_device_profiling_options_requests_lightweight_statistics(
options)) {
resolved_options.data_families =
iree_hal_vulkan_logical_device_lightweight_statistics_data_families();
}
if (iree_hal_device_profiling_options_requests_data(
&resolved_options,
IREE_HAL_DEVICE_PROFILING_DATA_QUEUE_EVENTS |
IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_QUEUE_EVENTS |
IREE_HAL_DEVICE_PROFILING_DATA_DISPATCH_EVENTS)) {
resolved_options.data_families |=
IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_METADATA;
}
resolved_options.flags &=
~IREE_HAL_DEVICE_PROFILING_FLAG_LIGHTWEIGHT_STATISTICS;
return resolved_options;
}
static iree_status_t
iree_hal_vulkan_logical_device_select_profile_host_time_domain(
const iree_hal_vulkan_logical_device_t* device,
VkTimeDomainEXT* out_time_domain) {
*out_time_domain = VK_TIME_DOMAIN_DEVICE_EXT;
const iree_hal_vulkan_time_domain_flags_t domains =
device->physical_device.calibrated_timestamp_time_domains;
if (!iree_all_bits_set(domains, IREE_HAL_VULKAN_TIME_DOMAIN_DEVICE)) {
return iree_make_status(
IREE_STATUS_UNAVAILABLE,
"Vulkan device queue profiling requires calibrated device timestamps");
}
if (iree_all_bits_set(domains, IREE_HAL_VULKAN_TIME_DOMAIN_CLOCK_MONOTONIC)) {
*out_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT;
return iree_ok_status();
}
if (iree_all_bits_set(domains,
IREE_HAL_VULKAN_TIME_DOMAIN_CLOCK_MONOTONIC_RAW)) {
*out_time_domain = VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT;
return iree_ok_status();
}
#if defined(IREE_PLATFORM_WINDOWS)
if (iree_all_bits_set(
domains, IREE_HAL_VULKAN_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER)) {
*out_time_domain = VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT;
return iree_ok_status();
}
#endif // defined(IREE_PLATFORM_WINDOWS)
return iree_make_status(
IREE_STATUS_UNAVAILABLE,
"Vulkan device queue profiling requires a calibrated host timestamp "
"domain compatible with IREE host time");
}
static iree_status_t
iree_hal_vulkan_logical_device_validate_queue_device_profiling(
const iree_hal_vulkan_logical_device_t* device,
VkTimeDomainEXT* out_time_domain) {
if (!iree_all_bits_set(
device->enabled_extensions,
IREE_HAL_VULKAN_DEVICE_EXTENSION_EXT_CALIBRATED_TIMESTAMPS)) {
return iree_make_status(IREE_STATUS_UNAVAILABLE,
"Vulkan device queue profiling requires "
"VK_EXT_calibrated_timestamps");
}
IREE_RETURN_IF_ERROR(
iree_hal_vulkan_logical_device_select_profile_host_time_domain(
device, out_time_domain));
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
const iree_hal_vulkan_queue_t* queue = &device->queues.lanes[i];
if (queue->timestamp_valid_bits != 64) {
return iree_make_status(
IREE_STATUS_UNAVAILABLE,
"Vulkan device queue profiling requires 64 valid timestamp bits on "
"queue family %u, but it reports %u",
queue->queue_family_index, queue->timestamp_valid_bits);
}
}
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_host_time_domain_frequency(
VkTimeDomainEXT time_domain, uint64_t* out_frequency_hz) {
*out_frequency_hz = 0;
switch (time_domain) {
default:
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"unsupported Vulkan calibrated host time domain %u",
(uint32_t)time_domain);
case VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT:
case VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT:
*out_frequency_hz = 1000000000ull;
return iree_ok_status();
#if defined(IREE_PLATFORM_WINDOWS)
case VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT: {
LARGE_INTEGER frequency;
if (!QueryPerformanceFrequency(&frequency) || frequency.QuadPart <= 0) {
return iree_make_status(
IREE_STATUS_FAILED_PRECONDITION,
"QueryPerformanceCounter frequency is unavailable");
}
*out_frequency_hz = (uint64_t)frequency.QuadPart;
return iree_ok_status();
}
#endif // defined(IREE_PLATFORM_WINDOWS)
}
}
static uint64_t iree_hal_vulkan_host_time_domain_timestamp_ns(
uint64_t timestamp, uint64_t frequency_hz) {
if (frequency_hz == 1000000000ull) return timestamp;
const uint64_t seconds = timestamp / frequency_hz;
const uint64_t remainder = timestamp % frequency_hz;
return seconds * 1000000000ull + (remainder * 1000000000ull) / frequency_hz;
}
static void iree_hal_vulkan_profile_clock_alignment_reset(
iree_hal_vulkan_profile_clock_alignment_t* clock_alignment) {
iree_slim_mutex_lock(&clock_alignment->mutex);
clock_alignment->minimum_clock_tick = UINT64_MAX;
clock_alignment->maximum_clock_tick = 0;
clock_alignment->minimum_event_tick = UINT64_MAX;
clock_alignment->maximum_event_tick = 0;
clock_alignment->has_clock_ticks = false;
clock_alignment->has_event_ticks = false;
clock_alignment->has_invalid_alignment = false;
iree_slim_mutex_unlock(&clock_alignment->mutex);
}
static bool iree_hal_vulkan_profile_clock_alignment_record_clock_tick(
iree_hal_vulkan_profile_clock_alignment_t* clock_alignment,
uint64_t calibrated_device_tick) {
iree_slim_mutex_lock(&clock_alignment->mutex);
if (clock_alignment->has_clock_ticks) {
clock_alignment->minimum_clock_tick =
iree_min(clock_alignment->minimum_clock_tick, calibrated_device_tick);
clock_alignment->maximum_clock_tick =
iree_max(clock_alignment->maximum_clock_tick, calibrated_device_tick);
} else {
clock_alignment->minimum_clock_tick = calibrated_device_tick;
clock_alignment->maximum_clock_tick = calibrated_device_tick;
clock_alignment->has_clock_ticks = true;
}
if (clock_alignment->has_event_ticks &&
(clock_alignment->minimum_event_tick <
clock_alignment->minimum_clock_tick ||
clock_alignment->maximum_event_tick >
clock_alignment->maximum_clock_tick)) {
clock_alignment->has_invalid_alignment = true;
}
const bool has_invalid_alignment = clock_alignment->has_invalid_alignment;
iree_slim_mutex_unlock(&clock_alignment->mutex);
return has_invalid_alignment;
}
static iree_status_t iree_hal_vulkan_logical_device_write_clock_correlation(
iree_hal_vulkan_logical_device_t* device) {
if (!iree_hal_local_profile_recorder_is_enabled(
device->profile.recorder,
IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_QUEUE_EVENTS |
IREE_HAL_DEVICE_PROFILING_DATA_DISPATCH_EVENTS)) {
return iree_ok_status();
}
uint64_t host_frequency_hz = 0;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_host_time_domain_frequency(
device->profile.host_time_domain, &host_frequency_hz));
VkCalibratedTimestampInfoEXT timestamp_infos[2] = {
{
.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT,
.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT,
},
{
.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT,
.timeDomain = device->profile.host_time_domain,
},
};
uint64_t timestamps[2] = {0, 0};
uint64_t max_deviation = 0;
const iree_time_t host_time_begin_ns = iree_time_now();
IREE_RETURN_IF_ERROR(iree_vkGetCalibratedTimestampsEXT(
IREE_VULKAN_DEVICE(&device->syms), device->logical_device,
IREE_ARRAYSIZE(timestamp_infos), timestamp_infos, timestamps,
&max_deviation));
const iree_time_t host_time_end_ns = iree_time_now();
(void)max_deviation;
const bool has_invalid_alignment =
iree_hal_vulkan_profile_clock_alignment_record_clock_tick(
&device->profile.clock_alignment, timestamps[0]);
iree_hal_profile_clock_correlation_record_t record =
iree_hal_profile_clock_correlation_record_default();
record.flags = IREE_HAL_PROFILE_CLOCK_CORRELATION_FLAG_DEVICE_TICK |
IREE_HAL_PROFILE_CLOCK_CORRELATION_FLAG_HOST_CPU_TIMESTAMP |
IREE_HAL_PROFILE_CLOCK_CORRELATION_FLAG_HOST_SYSTEM_TIMESTAMP |
IREE_HAL_PROFILE_CLOCK_CORRELATION_FLAG_HOST_TIME_BRACKET;
if (has_invalid_alignment) {
record.flags |=
IREE_HAL_PROFILE_CLOCK_CORRELATION_FLAG_DEVICE_TICK_UNALIGNED;
}
record.physical_device_ordinal =
device->topology_info.topology ? device->topology_info.topology_index : 0;
record.sample_id = ++device->profile.next_clock_correlation_sample_id;
record.device_tick = timestamps[0];
record.host_cpu_timestamp_ns = iree_hal_vulkan_host_time_domain_timestamp_ns(
timestamps[1], host_frequency_hz);
record.host_system_timestamp = timestamps[1];
record.host_system_frequency_hz = host_frequency_hz;
record.host_time_begin_ns = host_time_begin_ns;
record.host_time_end_ns = host_time_end_ns;
return iree_hal_local_profile_recorder_write_clock_correlations(
device->profile.recorder, 1, &record);
}
static iree_hal_local_profile_queue_scope_t
iree_hal_vulkan_logical_device_profile_queue_scope(
const iree_hal_vulkan_logical_device_t* device, uint32_t queue_ordinal) {
const uint32_t physical_device_ordinal =
device->topology_info.topology ? device->topology_info.topology_index : 0;
return (iree_hal_local_profile_queue_scope_t){
.physical_device_ordinal = physical_device_ordinal,
.queue_ordinal = queue_ordinal,
.stream_id = ((uint64_t)physical_device_ordinal << 32) | queue_ordinal,
};
}
static void iree_hal_vulkan_logical_device_clear_topology_info(
iree_hal_vulkan_logical_device_t* device) {
if (device->frontier_tracker) {
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
iree_hal_vulkan_queue_retire_frontier(&device->queues.lanes[i]);
}
iree_async_frontier_tracker_release(device->frontier_tracker);
device->frontier_tracker = NULL;
device->axis = 0;
}
memset(&device->topology_info, 0, sizeof(device->topology_info));
}
static void iree_hal_vulkan_logical_device_destroy(
iree_hal_device_t* base_device) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_allocator_t host_allocator = device->host_allocator;
IREE_TRACE_ZONE_BEGIN(z0);
IREE_ASSERT(!device->profile.recorder,
"profiling sessions must be ended before device destruction");
iree_hal_vulkan_logical_device_clear_topology_info(device);
iree_hal_channel_provider_release(device->channel_provider);
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
iree_hal_vulkan_queue_deinitialize(&device->queues.lanes[i]);
}
iree_hal_allocator_release(device->device_allocator);
iree_hal_vulkan_builtins_deinitialize(&device->builtins);
iree_async_proactor_pool_release(device->proactor_pool);
if (device->logical_device && device->owns_logical_device) {
iree_vkDestroyDevice(IREE_VULKAN_DEVICE(&device->syms),
device->logical_device, /*pAllocator=*/NULL);
}
iree_hal_vulkan_physical_device_snapshot_deinitialize(
host_allocator, &device->physical_device);
if (device->owns_instance) {
iree_hal_vulkan_instance_deinitialize(&device->instance);
}
iree_hal_vulkan_libvulkan_deinitialize(&device->libvulkan);
iree_arena_block_pool_deinitialize(&device->command_buffer_block_pool);
iree_slim_mutex_deinitialize(&device->profile.clock_alignment.mutex);
iree_slim_mutex_deinitialize(&device->queues.handle_mutexes.sparse_binding);
iree_slim_mutex_deinitialize(&device->queues.handle_mutexes.transfer);
iree_slim_mutex_deinitialize(&device->queues.handle_mutexes.compute);
iree_allocator_free(host_allocator, device);
IREE_TRACE_ZONE_END(z0);
}
static iree_string_view_t iree_hal_vulkan_logical_device_id(
iree_hal_device_t* base_device) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
return device->identifier;
}
static iree_allocator_t iree_hal_vulkan_logical_device_host_allocator(
iree_hal_device_t* base_device) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
return device->host_allocator;
}
static iree_hal_allocator_t* iree_hal_vulkan_logical_device_allocator(
iree_hal_device_t* base_device) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
return device->device_allocator;
}
static void iree_hal_vulkan_replace_device_allocator(
iree_hal_device_t* base_device, iree_hal_allocator_t* new_allocator) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_allocator_retain(new_allocator);
iree_hal_allocator_release(device->device_allocator);
device->device_allocator = new_allocator;
}
static void iree_hal_vulkan_replace_channel_provider(
iree_hal_device_t* base_device, iree_hal_channel_provider_t* new_provider) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_channel_provider_retain(new_provider);
iree_hal_channel_provider_release(device->channel_provider);
device->channel_provider = new_provider;
}
static iree_status_t iree_hal_vulkan_logical_device_trim(
iree_hal_device_t* base_device) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
iree_hal_vulkan_queue_trim(&device->queues.lanes[i]);
}
iree_arena_block_pool_trim(&device->command_buffer_block_pool);
return iree_hal_allocator_trim(device->device_allocator);
}
static bool iree_hal_vulkan_logical_device_query_queue_i64(
iree_hal_vulkan_logical_device_t* device, iree_string_view_t category,
iree_string_view_t key, int64_t* out_value) {
int64_t total_value = 0;
bool has_value = false;
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
int64_t queue_value = 0;
if (iree_hal_vulkan_queue_query_i64(&device->queues.lanes[i], category, key,
&queue_value)) {
has_value = true;
if (queue_value > INT64_MAX - total_value) {
total_value = INT64_MAX;
} else {
total_value += queue_value;
}
}
}
*out_value = total_value;
return has_value;
}
static int64_t iree_hal_vulkan_feature_query_value(
iree_hal_vulkan_features_t enabled_features,
iree_hal_vulkan_features_t feature_bit) {
return iree_all_bits_set(enabled_features, feature_bit) ? 1 : 0;
}
static iree_status_t iree_hal_vulkan_logical_device_query_i64(
iree_hal_device_t* base_device, iree_string_view_t category,
iree_string_view_t key, int64_t* out_value) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
*out_value = 0;
if (iree_string_view_equal(category, IREE_SV("hal.device.id"))) {
*out_value =
iree_string_view_match_pattern(device->identifier, key) ? 1 : 0;
return iree_ok_status();
}
if (iree_string_view_equal(category, IREE_SV("hal.executable.format"))) {
*out_value =
iree_hal_vulkan_executable_format_supported(
device->enabled_features, device->enabled_dispatch_abis, key)
? 1
: 0;
return iree_ok_status();
}
if (iree_string_view_equal(category, IREE_SV("hal.device"))) {
if (iree_string_view_equal(key, IREE_SV("concurrency"))) {
*out_value = (int64_t)device->queues.count;
return iree_ok_status();
}
} else if (iree_string_view_equal(category, IREE_SV("hal.dispatch"))) {
if (iree_string_view_equal(key, IREE_SV("concurrency"))) {
*out_value = 1;
return iree_ok_status();
}
} else if (iree_string_view_equal(category, IREE_SV("vulkan.device"))) {
if (iree_string_view_equal(key, IREE_SV("api_version"))) {
*out_value = device->physical_device.properties2.properties.apiVersion;
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("subgroup_size"))) {
*out_value = device->physical_device.subgroup_properties.subgroupSize;
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("max_compute_workgroup_invocations"))) {
*out_value = device->physical_device.properties2.properties.limits
.maxComputeWorkGroupInvocations;
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("max_compute_workgroup_size_x"))) {
*out_value = device->physical_device.properties2.properties.limits
.maxComputeWorkGroupSize[0];
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("max_compute_workgroup_size_y"))) {
*out_value = device->physical_device.properties2.properties.limits
.maxComputeWorkGroupSize[1];
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("max_compute_workgroup_size_z"))) {
*out_value = device->physical_device.properties2.properties.limits
.maxComputeWorkGroupSize[2];
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("max_compute_workgroup_count_x"))) {
*out_value = device->physical_device.properties2.properties.limits
.maxComputeWorkGroupCount[0];
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("max_compute_workgroup_count_y"))) {
*out_value = device->physical_device.properties2.properties.limits
.maxComputeWorkGroupCount[1];
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("max_compute_workgroup_count_z"))) {
*out_value = device->physical_device.properties2.properties.limits
.maxComputeWorkGroupCount[2];
return iree_ok_status();
}
} else if (iree_string_view_equal(category, IREE_SV("vulkan.feature"))) {
if (iree_string_view_equal(key, IREE_SV("buffer_device_address")) ||
iree_string_view_equal(key, IREE_SV("physical_storage_buffer64"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_BUFFER_DEVICE_ADDRESSES);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("subgroup_size_control"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SUBGROUP_SIZE_CONTROL);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("cooperative_matrix_khr"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_COOPERATIVE_MATRIX);
return iree_ok_status();
} else if (iree_string_view_equal(key,
IREE_SV("storage_buffer_8bit_access"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_STORAGE_BUFFER_8BIT_ACCESS);
return iree_ok_status();
} else if (iree_string_view_equal(key,
IREE_SV("storage_buffer_16bit_access"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_STORAGE_BUFFER_16BIT_ACCESS);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("shader_float16"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_FLOAT16);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("shader_float64"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_FLOAT64);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("shader_bfloat16_type"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_BFLOAT16_TYPE);
return iree_ok_status();
} else if (iree_string_view_equal(key,
IREE_SV("shader_bfloat16_dot_product"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_BFLOAT16_DOT_PRODUCT);
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("shader_bfloat16_cooperative_matrix"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_BFLOAT16_COOPERATIVE_MATRIX);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("shader_int8"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features, IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_INT8);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("shader_int16"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_INT16);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("shader_int64"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_INT64);
return iree_ok_status();
} else if (iree_string_view_equal(key,
IREE_SV("shader_integer_dot_product"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_SHADER_INTEGER_DOT_PRODUCT);
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("vulkan_memory_model"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_VULKAN_MEMORY_MODEL);
return iree_ok_status();
} else if (iree_string_view_equal(
key, IREE_SV("vulkan_memory_model_device_scope"))) {
*out_value = iree_hal_vulkan_feature_query_value(
device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_VULKAN_MEMORY_MODEL_DEVICE_SCOPE);
return iree_ok_status();
}
} else if (iree_string_view_equal(category,
IREE_SV("vulkan.cooperative_matrix"))) {
const bool has_cooperative_matrix =
iree_all_bits_set(device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_COOPERATIVE_MATRIX);
if (iree_string_view_equal(key, IREE_SV("supported"))) {
*out_value = has_cooperative_matrix ? 1 : 0;
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("property_count"))) {
*out_value =
has_cooperative_matrix
? device->physical_device.cooperative_matrix_property_count
: 0;
return iree_ok_status();
} else if (iree_string_view_equal(key, IREE_SV("supported_stages"))) {
*out_value = has_cooperative_matrix
? device->physical_device.cooperative_matrix_properties
.cooperativeMatrixSupportedStages
: 0;
return iree_ok_status();
}
} else if (iree_hal_vulkan_logical_device_query_queue_i64(device, category,
key, out_value)) {
return iree_ok_status();
}
return iree_make_status(
IREE_STATUS_NOT_FOUND,
"unknown device configuration key value '%.*s :: %.*s'",
(int)category.size, category.data, (int)key.size, key.data);
}
IREE_API_EXPORT iree_status_t
iree_hal_vulkan_device_query_cooperative_matrix_properties(
iree_hal_device_t* base_device, iree_host_size_t property_capacity,
iree_host_size_t* out_property_count,
iree_hal_vulkan_cooperative_matrix_property_t* out_properties) {
IREE_ASSERT_ARGUMENT(base_device);
IREE_ASSERT_ARGUMENT(out_property_count);
*out_property_count = 0;
if (property_capacity != 0 && out_properties == NULL) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"Vulkan cooperative matrix property storage is "
"NULL for capacity %" PRIhsz,
property_capacity);
}
if (!iree_hal_vulkan_logical_device_isa(base_device)) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"device is not a Vulkan logical device");
}
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
if (!iree_all_bits_set(device->enabled_features,
IREE_HAL_VULKAN_FEATURE_ENABLE_COOPERATIVE_MATRIX)) {
return iree_ok_status();
}
const iree_host_size_t property_count =
device->physical_device.cooperative_matrix_property_count;
*out_property_count = property_count;
if (out_properties == NULL) {
return iree_ok_status();
}
if (property_capacity < property_count) {
return iree_make_status(
IREE_STATUS_OUT_OF_RANGE,
"Vulkan cooperative matrix property capacity %" PRIhsz
" is smaller than required count %" PRIhsz,
property_capacity, property_count);
}
const VkCooperativeMatrixPropertiesKHR* source_properties =
device->physical_device.cooperative_matrix_property_rows;
if (property_count != 0 && source_properties == NULL) {
return iree_make_status(IREE_STATUS_INTERNAL,
"Vulkan cooperative matrix support has %" PRIhsz
" property rows but no retained row storage",
property_count);
}
for (iree_host_size_t i = 0; i < property_count; ++i) {
out_properties[i] = (iree_hal_vulkan_cooperative_matrix_property_t){
.m_size = source_properties[i].MSize,
.n_size = source_properties[i].NSize,
.k_size = source_properties[i].KSize,
.a_type = source_properties[i].AType,
.b_type = source_properties[i].BType,
.c_type = source_properties[i].CType,
.result_type = source_properties[i].ResultType,
.saturating_accumulation = source_properties[i].saturatingAccumulation,
.scope = source_properties[i].scope,
};
}
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_logical_device_query_capabilities(
iree_hal_device_t* base_device,
iree_hal_device_capabilities_t* out_capabilities) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
memset(out_capabilities, 0, sizeof(*out_capabilities));
memcpy(out_capabilities->physical_device_uuid,
device->physical_device.id_properties.deviceUUID,
sizeof(out_capabilities->physical_device_uuid));
out_capabilities->has_physical_device_uuid = true;
out_capabilities->driver_device_handle =
(uintptr_t)device->physical_device.handle;
out_capabilities->flags |= IREE_HAL_DEVICE_CAPABILITY_TIMELINE_SEMAPHORES |
IREE_HAL_DEVICE_CAPABILITY_ATOMIC_SCOPE_DEVICE;
if (iree_all_bits_set(
device->enabled_extensions,
IREE_HAL_VULKAN_DEVICE_EXTENSION_KHR_EXTERNAL_MEMORY_FD)) {
out_capabilities->buffer_export_types |=
IREE_HAL_TOPOLOGY_HANDLE_TYPE_OPAQUE_FD;
out_capabilities->buffer_import_types |=
IREE_HAL_TOPOLOGY_HANDLE_TYPE_OPAQUE_FD;
}
return iree_ok_status();
}
static const iree_hal_device_topology_info_t*
iree_hal_vulkan_logical_device_topology_info(iree_hal_device_t* base_device) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
return &device->topology_info;
}
static iree_status_t iree_hal_vulkan_logical_device_refine_topology_edge(
iree_hal_device_t* source_device, iree_hal_device_t* target_device,
iree_hal_topology_edge_t* edge) {
(void)source_device;
(void)target_device;
(void)edge;
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_logical_device_assign_topology_info(
iree_hal_device_t* base_device,
const iree_hal_device_topology_info_t* topology_info) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
if (!topology_info) {
iree_hal_vulkan_logical_device_clear_topology_info(device);
return iree_ok_status();
}
iree_async_frontier_tracker_t* frontier_tracker =
topology_info->frontier.tracker;
iree_async_axis_t base_axis = topology_info->frontier.base_axis;
const uint8_t session_epoch = iree_async_axis_session(base_axis);
const uint8_t machine_index = iree_async_axis_machine(base_axis);
const uint8_t device_index = iree_async_axis_device_index(base_axis);
iree_status_t status = iree_ok_status();
iree_host_size_t assigned_queue_count = 0;
for (iree_host_size_t i = 0;
i < device->queues.lane_count && iree_status_is_ok(status); ++i) {
iree_async_axis_t queue_axis = iree_async_axis_make_queue(
session_epoch, machine_index, device_index, (uint8_t)i);
status = iree_hal_vulkan_queue_assign_frontier(
&device->queues.lanes[i], frontier_tracker, queue_axis);
if (iree_status_is_ok(status)) assigned_queue_count = i + 1;
}
if (!iree_status_is_ok(status)) {
for (iree_host_size_t i = 0; i < assigned_queue_count; ++i) {
iree_hal_vulkan_queue_retire_frontier(&device->queues.lanes[i]);
}
return status;
}
device->topology_info = *topology_info;
device->frontier_tracker = frontier_tracker;
device->axis = base_axis;
iree_async_frontier_tracker_retain(device->frontier_tracker);
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_logical_device_create_channel(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
iree_hal_channel_params_t params, iree_hal_channel_t** out_channel) {
(void)base_device;
(void)queue_affinity;
(void)params;
*out_channel = NULL;
return iree_hal_vulkan_unimplemented(IREE_SV("collective channels"));
}
static iree_status_t iree_hal_vulkan_logical_device_queue_lane_for_role(
iree_hal_vulkan_logical_device_t* device, iree_hal_vulkan_queue_role_t role,
iree_hal_vulkan_queue_t** out_queue) {
IREE_ASSERT_ARGUMENT(out_queue);
switch (role) {
case IREE_HAL_VULKAN_QUEUE_ROLE_COMPUTE:
*out_queue = device->queues.compute_lane;
return iree_ok_status();
case IREE_HAL_VULKAN_QUEUE_ROLE_TRANSFER:
*out_queue = device->queues.transfer_lane;
return iree_ok_status();
case IREE_HAL_VULKAN_QUEUE_ROLE_SPARSE_BINDING:
*out_queue = device->queues.sparse_binding_lane;
return iree_ok_status();
}
*out_queue = NULL;
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"unrecognized Vulkan queue role %u", (uint32_t)role);
}
static bool iree_hal_vulkan_queue_lane_matches_affinity(
const iree_hal_vulkan_queue_t* queue,
iree_hal_queue_affinity_t queue_affinity, VkQueueFlags required_flags) {
return !iree_hal_queue_affinity_is_empty(queue->queue_affinity) &&
iree_any_bit_set(queue_affinity, queue->queue_affinity) &&
iree_all_bits_set(queue->queue_flags, required_flags);
}
static iree_hal_vulkan_queue_role_t
iree_hal_vulkan_logical_device_preferred_queue_role_for_command_categories(
iree_hal_command_category_t command_categories) {
if (iree_any_bit_set(command_categories,
IREE_HAL_COMMAND_CATEGORY_DISPATCH)) {
return IREE_HAL_VULKAN_QUEUE_ROLE_COMPUTE;
}
return IREE_HAL_VULKAN_QUEUE_ROLE_TRANSFER;
}
static VkQueueFlags
iree_hal_vulkan_logical_device_required_queue_flags_for_command_categories(
iree_hal_command_category_t command_categories) {
VkQueueFlags queue_flags = 0;
if (iree_any_bit_set(command_categories,
IREE_HAL_COMMAND_CATEGORY_DISPATCH)) {
queue_flags |= VK_QUEUE_COMPUTE_BIT;
}
if (iree_any_bit_set(command_categories,
IREE_HAL_COMMAND_CATEGORY_TRANSFER)) {
// Transfer command buffers may record unaligned fills/updates, which are
// emulated with compute built-ins (see the fill/update replay paths in
// command_buffer.c). The queue family is fixed before those dispatches are
// recorded, so a transfer command buffer must be placed on a
// compute-capable lane. Recording the built-in dispatch onto a
// transfer-only is invalid – that will hang any semaphore waiting on the
// submission.
queue_flags |= VK_QUEUE_TRANSFER_BIT | VK_QUEUE_COMPUTE_BIT;
}
return queue_flags;
}
static iree_status_t
iree_hal_vulkan_logical_device_select_queue_lane_from_normalized_affinity(
iree_hal_vulkan_logical_device_t* device,
iree_hal_vulkan_queue_role_t preferred_role, VkQueueFlags required_flags,
iree_hal_queue_affinity_t normalized_queue_affinity,
iree_hal_vulkan_queue_t** out_queue) {
IREE_ASSERT_ARGUMENT(out_queue);
*out_queue = NULL;
iree_hal_vulkan_queue_t* preferred_queue = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_queue_lane_for_role(
device, preferred_role, &preferred_queue));
if (preferred_queue &&
iree_hal_vulkan_queue_lane_matches_affinity(
preferred_queue, normalized_queue_affinity, required_flags)) {
*out_queue = preferred_queue;
return iree_ok_status();
}
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
iree_hal_vulkan_queue_t* queue = &device->queues.lanes[i];
if (!iree_hal_vulkan_queue_lane_matches_affinity(
queue, normalized_queue_affinity, required_flags)) {
continue;
}
*out_queue = queue;
return iree_ok_status();
}
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"no Vulkan queue lane matches affinity 0x%016" PRIx64
" with required queue flags 0x%08x",
normalized_queue_affinity, required_flags);
}
static iree_status_t iree_hal_vulkan_logical_device_select_queue_lane(
iree_hal_vulkan_logical_device_t* device,
iree_hal_vulkan_queue_role_t preferred_role, VkQueueFlags required_flags,
iree_hal_queue_affinity_t queue_affinity,
iree_hal_vulkan_queue_t** out_queue) {
IREE_RETURN_IF_ERROR(iree_hal_vulkan_queue_affinity_normalize(
device->queues.affinity_mask, queue_affinity, &queue_affinity));
return iree_hal_vulkan_logical_device_select_queue_lane_from_normalized_affinity(
device, preferred_role, required_flags, queue_affinity, out_queue);
}
static iree_status_t
iree_hal_vulkan_logical_device_resolve_command_buffer_queue_affinity(
iree_hal_vulkan_logical_device_t* device,
iree_hal_command_buffer_t* command_buffer,
iree_hal_queue_affinity_t queue_affinity,
iree_hal_queue_affinity_t* out_queue_affinity) {
IREE_RETURN_IF_ERROR(iree_hal_vulkan_queue_affinity_normalize(
device->queues.affinity_mask, queue_affinity, &queue_affinity));
const iree_hal_queue_affinity_t command_buffer_queue_affinity =
iree_hal_command_buffer_queue_affinity(command_buffer);
iree_hal_queue_affinity_t resolved_queue_affinity = queue_affinity;
iree_hal_queue_affinity_and_into(resolved_queue_affinity,
command_buffer_queue_affinity);
if (iree_hal_queue_affinity_is_empty(resolved_queue_affinity)) {
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"queue_execute affinity does not match command buffer affinity "
"(queue=0x%016" PRIx64 ", command_buffer=0x%016" PRIx64 ")",
queue_affinity, command_buffer_queue_affinity);
}
*out_queue_affinity = resolved_queue_affinity;
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_logical_device_create_command_buffer(
iree_hal_device_t* base_device, iree_hal_command_buffer_mode_t mode,
iree_hal_command_category_t command_categories,
iree_hal_queue_affinity_t queue_affinity, iree_host_size_t binding_capacity,
iree_hal_command_buffer_t** out_command_buffer) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_vulkan_queue_t* queue = NULL;
const iree_hal_vulkan_queue_role_t preferred_role =
iree_hal_vulkan_logical_device_preferred_queue_role_for_command_categories(
command_categories);
const VkQueueFlags required_queue_flags =
iree_hal_vulkan_logical_device_required_queue_flags_for_command_categories(
command_categories);
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, preferred_role, required_queue_flags, queue_affinity, &queue));
return iree_hal_vulkan_command_buffer_create(
device->device_allocator, mode, command_categories, queue->queue_affinity,
binding_capacity, &device->command_buffer_block_pool,
device->host_allocator, out_command_buffer);
}
static iree_status_t iree_hal_vulkan_logical_device_create_event(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
iree_hal_event_flags_t flags, iree_hal_event_t** out_event) {
(void)base_device;
(void)queue_affinity;
(void)flags;
*out_event = NULL;
return iree_hal_vulkan_unimplemented(IREE_SV("events"));
}
static iree_status_t iree_hal_vulkan_logical_device_create_executable_cache(
iree_hal_device_t* base_device, iree_string_view_t identifier,
iree_hal_executable_cache_t** out_executable_cache) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
return iree_hal_vulkan_executable_cache_create(
&device->syms, device->logical_device, &device->physical_device,
device->enabled_features, device->enabled_extensions, identifier,
device->enabled_dispatch_abis, device->host_allocator,
out_executable_cache);
}
static iree_status_t iree_hal_vulkan_logical_device_import_file(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
iree_hal_memory_access_t access, iree_io_file_handle_t* handle,
iree_hal_external_file_flags_t flags, iree_hal_file_t** out_file) {
IREE_ASSERT_ARGUMENT(out_file);
*out_file = NULL;
if (flags != IREE_HAL_EXTERNAL_FILE_FLAG_NONE) {
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"unsupported Vulkan external file flags: 0x%" PRIx32, flags);
}
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
IREE_RETURN_IF_ERROR(iree_hal_vulkan_queue_affinity_normalize(
device->queues.affinity_mask, queue_affinity, &queue_affinity));
iree_hal_file_t* file = NULL;
iree_status_t status = iree_hal_file_from_handle(
device->device_allocator, queue_affinity, access, handle,
device->proactor, device->host_allocator, &file);
if (iree_status_is_ok(status) &&
iree_io_file_handle_type(handle) == IREE_IO_FILE_HANDLE_TYPE_FD &&
!iree_hal_file_storage_buffer(file) &&
!iree_hal_file_async_handle(file)) {
status = iree_make_status(
IREE_STATUS_UNIMPLEMENTED,
"Vulkan fd file transfers require a proactor-backed async file handle");
}
if (iree_status_is_ok(status)) {
*out_file = file;
} else {
iree_hal_file_release(file);
}
return status;
}
static iree_status_t iree_hal_vulkan_logical_device_create_semaphore(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
uint64_t initial_value, iree_hal_semaphore_flags_t flags,
iree_hal_semaphore_t** out_semaphore) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
IREE_RETURN_IF_ERROR(iree_hal_vulkan_queue_affinity_normalize(
device->queues.affinity_mask, queue_affinity, &queue_affinity));
return iree_hal_vulkan_semaphore_create(
device, &device->syms, device->logical_device, device->proactor,
queue_affinity, initial_value, flags, device->host_allocator,
out_semaphore);
}
static iree_hal_semaphore_compatibility_t
iree_hal_vulkan_logical_device_query_semaphore_compatibility(
iree_hal_device_t* base_device, iree_hal_semaphore_t* semaphore) {
(void)base_device;
if (iree_hal_vulkan_semaphore_isa(semaphore)) {
return IREE_HAL_SEMAPHORE_COMPATIBILITY_ALL;
}
return IREE_HAL_SEMAPHORE_COMPATIBILITY_HOST_ONLY;
}
static iree_status_t iree_hal_vulkan_logical_device_query_queue_pool_backend(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
iree_hal_queue_pool_backend_t* out_backend) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
IREE_RETURN_IF_ERROR(iree_hal_vulkan_allocator_query_queue_pool_backend(
device->device_allocator, queue_affinity, out_backend));
out_backend->epoch_query = (iree_hal_pool_epoch_query_t){
.fn = iree_hal_vulkan_logical_device_query_pool_epoch,
.user_data = device,
};
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_logical_device_queue_alloca(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_pool_t* pool, iree_hal_buffer_params_t params,
iree_device_size_t allocation_size, iree_hal_alloca_flags_t flags,
iree_hal_buffer_t** IREE_RESTRICT out_buffer) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
*out_buffer = NULL;
const iree_device_size_t byte_length = allocation_size;
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, IREE_HAL_VULKAN_QUEUE_ROLE_COMPUTE, /*required_flags=*/0,
queue_affinity, &queue));
const iree_hal_queue_affinity_t allocation_queue_affinity =
queue->queue_affinity;
iree_hal_vulkan_queue_alloca_plan_t allocation_plan;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_allocator_select_queue_alloca_plan(
device->device_allocator, pool, &params, &allocation_size,
&allocation_plan));
params.queue_affinity = allocation_queue_affinity;
if (allocation_plan.strategy ==
IREE_HAL_VULKAN_QUEUE_ALLOCA_STRATEGY_SPARSE &&
!iree_all_bits_set(queue->queue_flags, VK_QUEUE_SPARSE_BINDING_BIT)) {
if (!device->queues.sparse_binding_lane) {
return iree_make_status(
IREE_STATUS_FAILED_PRECONDITION,
"Vulkan sparse queue_alloca requires a sparse-binding queue");
}
queue = device->queues.sparse_binding_lane;
}
return iree_hal_vulkan_queue_submit_alloca(
queue, wait_semaphore_list, signal_semaphore_list, allocation_plan,
params, allocation_size, byte_length, flags, out_buffer);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_dealloca(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_buffer_t* buffer, iree_hal_dealloca_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, IREE_HAL_VULKAN_QUEUE_ROLE_COMPUTE, /*required_flags=*/0,
queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_dealloca(
queue, wait_semaphore_list, signal_semaphore_list, buffer, flags);
}
// Selects the queue lane for a native fill/update. Fills and updates are
// transfer-category operations, but their unaligned path is emulated with a
// compute built-in.
static iree_status_t
iree_hal_vulkan_logical_device_select_transfer_operation_lane(
iree_hal_vulkan_logical_device_t* device,
iree_hal_queue_affinity_t queue_affinity,
iree_hal_vulkan_queue_t** out_queue) {
const iree_hal_command_category_t command_categories =
IREE_HAL_COMMAND_CATEGORY_TRANSFER;
const iree_hal_vulkan_queue_role_t preferred_role =
iree_hal_vulkan_logical_device_preferred_queue_role_for_command_categories(
command_categories);
const VkQueueFlags required_flags =
iree_hal_vulkan_logical_device_required_queue_flags_for_command_categories(
command_categories);
// Prefer a lane within the requested affinity.
iree_status_t status = iree_hal_vulkan_logical_device_select_queue_lane(
device, preferred_role, required_flags, queue_affinity, out_queue);
if (iree_status_is_ok(status)) return status;
// Retry with any queue affinity - the operation may need the compute built-in
// (e.g. an unaligned fill/update).
iree_status_ignore(status);
return iree_hal_vulkan_logical_device_select_queue_lane(
device, preferred_role, required_flags, IREE_HAL_QUEUE_AFFINITY_ANY,
out_queue);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_fill(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_buffer_t* target_buffer, iree_device_size_t target_offset,
iree_device_size_t length, const void* pattern,
iree_host_size_t pattern_length, iree_hal_fill_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(
iree_hal_vulkan_logical_device_select_transfer_operation_lane(
device, queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_fill(
queue, wait_semaphore_list, signal_semaphore_list, target_buffer,
target_offset, length, pattern, pattern_length, flags);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_update(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
const void* source_buffer, iree_host_size_t source_offset,
iree_hal_buffer_t* target_buffer, iree_device_size_t target_offset,
iree_device_size_t length, iree_hal_update_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(
iree_hal_vulkan_logical_device_select_transfer_operation_lane(
device, queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_update(
queue, wait_semaphore_list, signal_semaphore_list, source_buffer,
source_offset, target_buffer, target_offset, length, flags);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_copy(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_buffer_t* source_buffer, iree_device_size_t source_offset,
iree_hal_buffer_t* target_buffer, iree_device_size_t target_offset,
iree_device_size_t length, iree_hal_copy_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, IREE_HAL_VULKAN_QUEUE_ROLE_TRANSFER, VK_QUEUE_TRANSFER_BIT,
queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_copy(
queue, wait_semaphore_list, signal_semaphore_list, source_buffer,
source_offset, target_buffer, target_offset, length, flags);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_read(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_file_t* source_file, uint64_t source_offset,
iree_hal_buffer_t* target_buffer, iree_device_size_t target_offset,
iree_device_size_t length, iree_hal_read_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, IREE_HAL_VULKAN_QUEUE_ROLE_TRANSFER, VK_QUEUE_TRANSFER_BIT,
queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_read(
queue, wait_semaphore_list, signal_semaphore_list, source_file,
source_offset, target_buffer, target_offset, length, flags);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_write(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_buffer_t* source_buffer, iree_device_size_t source_offset,
iree_hal_file_t* target_file, uint64_t target_offset,
iree_device_size_t length, iree_hal_write_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, IREE_HAL_VULKAN_QUEUE_ROLE_TRANSFER, VK_QUEUE_TRANSFER_BIT,
queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_write(
queue, wait_semaphore_list, signal_semaphore_list, source_buffer,
source_offset, target_file, target_offset, length, flags);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_host_call(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_host_call_t call, const uint64_t args[4],
iree_hal_host_call_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, IREE_HAL_VULKAN_QUEUE_ROLE_COMPUTE, /*required_flags=*/0,
queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_host_call(
queue, wait_semaphore_list, signal_semaphore_list, call, args, flags);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_dispatch(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_executable_t* executable,
iree_hal_executable_function_t function_ordinal,
const iree_hal_dispatch_config_t config, iree_const_byte_span_t constants,
const iree_hal_buffer_ref_list_t bindings,
iree_hal_dispatch_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
if (!iree_hal_vulkan_executable_isa(executable)) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"queue_dispatch executable is not a Vulkan "
"executable");
}
if (bindings.count != 0 && !bindings.values) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"queue_dispatch binding storage is NULL");
}
for (iree_host_size_t i = 0; i < bindings.count; ++i) {
if (!bindings.values[i].buffer) {
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"queue_dispatch binding %" PRIhsz
" is indirect; direct queue dispatch has no binding table",
i);
}
}
iree_hal_vulkan_queue_t* queue = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, IREE_HAL_VULKAN_QUEUE_ROLE_COMPUTE, VK_QUEUE_COMPUTE_BIT,
queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_dispatch(
queue, wait_semaphore_list, signal_semaphore_list, executable,
function_ordinal, config, constants, bindings, flags);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_execute(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity,
const iree_hal_semaphore_list_t wait_semaphore_list,
const iree_hal_semaphore_list_t signal_semaphore_list,
iree_hal_command_buffer_t* command_buffer,
iree_hal_buffer_binding_table_t binding_table,
iree_hal_execute_flags_t flags) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
if (iree_any_bit_set(
flags, ~(iree_hal_execute_flags_t)
IREE_HAL_EXECUTE_FLAG_BORROW_BINDING_TABLE_LIFETIME)) {
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"unsupported Vulkan queue execute flags: 0x%" PRIx64, flags);
}
if (command_buffer) {
if (!iree_hal_vulkan_command_buffer_isa(command_buffer)) {
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"queue_execute command buffer is not a Vulkan "
"command buffer");
}
}
const bool is_empty_command_buffer =
!command_buffer ||
iree_hal_vulkan_command_buffer_is_empty(command_buffer);
if (is_empty_command_buffer && binding_table.count != 0) {
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"barrier-only queue_execute must not provide a binding table "
"(count=%" PRIhsz ")",
binding_table.count);
}
iree_hal_vulkan_queue_t* queue = NULL;
const iree_hal_vulkan_queue_role_t preferred_role =
is_empty_command_buffer
? IREE_HAL_VULKAN_QUEUE_ROLE_COMPUTE
: iree_hal_vulkan_logical_device_preferred_queue_role_for_command_categories(
iree_hal_command_buffer_allowed_categories(command_buffer));
const VkQueueFlags required_queue_flags =
is_empty_command_buffer
? 0
: iree_hal_vulkan_logical_device_required_queue_flags_for_command_categories(
iree_hal_command_buffer_allowed_categories(command_buffer));
if (is_empty_command_buffer) {
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_select_queue_lane(
device, preferred_role, required_queue_flags, queue_affinity, &queue));
return iree_hal_vulkan_queue_submit_barrier(queue, wait_semaphore_list,
signal_semaphore_list);
}
IREE_RETURN_IF_ERROR(
iree_hal_vulkan_logical_device_resolve_command_buffer_queue_affinity(
device, command_buffer, queue_affinity, &queue_affinity));
IREE_RETURN_IF_ERROR(
iree_hal_vulkan_logical_device_select_queue_lane_from_normalized_affinity(
device, preferred_role, required_queue_flags, queue_affinity,
&queue));
return iree_hal_vulkan_queue_submit_execute(
queue, wait_semaphore_list, signal_semaphore_list, command_buffer,
binding_table, flags, IREE_HAL_PROFILE_QUEUE_EVENT_TYPE_EXECUTE);
}
static iree_status_t iree_hal_vulkan_logical_device_queue_flush(
iree_hal_device_t* base_device, iree_hal_queue_affinity_t queue_affinity) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
IREE_RETURN_IF_ERROR(iree_hal_vulkan_queue_affinity_normalize(
device->queues.affinity_mask, queue_affinity, &queue_affinity));
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
if (iree_any_bit_set(queue_affinity,
device->queues.lanes[i].queue_affinity)) {
iree_hal_vulkan_queue_drain_completions(&device->queues.lanes[i]);
}
}
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_logical_device_profiling_begin(
iree_hal_device_t* base_device,
const iree_hal_device_profiling_options_t* options) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
if (device->profile.recorder) {
return iree_make_status(IREE_STATUS_FAILED_PRECONDITION,
"cannot nest Vulkan profile captures");
}
const iree_hal_device_profiling_options_t resolved_options =
iree_hal_vulkan_logical_device_resolve_profiling_options(options);
const iree_hal_device_profiling_data_families_t supported_data_families =
IREE_HAL_DEVICE_PROFILING_DATA_QUEUE_EVENTS |
IREE_HAL_DEVICE_PROFILING_DATA_DISPATCH_EVENTS |
IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_METADATA |
IREE_HAL_DEVICE_PROFILING_DATA_MEMORY_EVENTS |
IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_QUEUE_EVENTS;
const iree_hal_device_profiling_data_families_t unsupported_data_families =
resolved_options.data_families & ~supported_data_families;
if (unsupported_data_families != IREE_HAL_DEVICE_PROFILING_DATA_NONE) {
return iree_make_status(
IREE_STATUS_UNIMPLEMENTED,
"unsupported Vulkan profiling data families 0x%" PRIx64,
unsupported_data_families);
}
VkTimeDomainEXT profile_host_time_domain = VK_TIME_DOMAIN_DEVICE_EXT;
const bool device_queue_events_enabled =
iree_hal_device_profiling_options_requests_data(
&resolved_options,
IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_QUEUE_EVENTS);
const bool dispatch_events_enabled =
iree_hal_device_profiling_options_requests_data(
&resolved_options, IREE_HAL_DEVICE_PROFILING_DATA_DISPATCH_EVENTS);
if (device_queue_events_enabled || dispatch_events_enabled) {
IREE_RETURN_IF_ERROR(
iree_hal_vulkan_logical_device_validate_queue_device_profiling(
device, &profile_host_time_domain));
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
IREE_RETURN_IF_ERROR(
iree_hal_vulkan_queue_prepare_profile_timestamp_queries(
&device->queues.lanes[i]));
}
}
iree_hal_vulkan_profile_clock_alignment_reset(
&device->profile.clock_alignment);
const uint32_t physical_device_ordinal =
device->topology_info.topology ? device->topology_info.topology_index : 0;
iree_hal_profile_device_record_t device_record =
iree_hal_profile_device_record_default();
device_record.physical_device_ordinal = physical_device_ordinal;
device_record.queue_count =
iree_hal_vulkan_logical_device_profile_count(device->queues.lane_count);
device_record.flags |= IREE_HAL_PROFILE_DEVICE_FLAG_PHYSICAL_DEVICE_UUID;
memcpy(device_record.physical_device_uuid,
device->physical_device.id_properties.deviceUUID,
sizeof(device_record.physical_device_uuid));
iree_hal_profile_queue_record_t* queue_records = NULL;
IREE_RETURN_IF_ERROR(iree_allocator_malloc_array(
device->host_allocator, device->queues.lane_count, sizeof(*queue_records),
(void**)&queue_records));
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
const uint32_t queue_ordinal =
iree_hal_vulkan_logical_device_profile_count(i);
const iree_hal_local_profile_queue_scope_t scope =
iree_hal_vulkan_logical_device_profile_queue_scope(device,
queue_ordinal);
queue_records[i] = iree_hal_profile_queue_record_default();
queue_records[i].physical_device_ordinal = scope.physical_device_ordinal;
queue_records[i].queue_ordinal = scope.queue_ordinal;
queue_records[i].stream_id = scope.stream_id;
}
iree_hal_local_profile_recorder_options_t recorder_options = {
.name = device->identifier,
.session_id = ++device->profile.next_session_id,
.device_record_count = 1,
.device_records = &device_record,
.queue_record_count = device->queues.lane_count,
.queue_records = queue_records,
.dispatch_event_capacity =
IREE_HAL_VULKAN_LOGICAL_DEVICE_PROFILE_DISPATCH_EVENT_CAPACITY,
.queue_event_capacity =
IREE_HAL_VULKAN_LOGICAL_DEVICE_PROFILE_QUEUE_EVENT_CAPACITY,
.producer_data_families =
(dispatch_events_enabled
? IREE_HAL_DEVICE_PROFILING_DATA_DISPATCH_EVENTS
: IREE_HAL_DEVICE_PROFILING_DATA_NONE) |
(device_queue_events_enabled
? IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_QUEUE_EVENTS
: IREE_HAL_DEVICE_PROFILING_DATA_NONE),
.queue_device_event_capacity =
IREE_HAL_VULKAN_LOGICAL_DEVICE_PROFILE_QUEUE_DEVICE_EVENT_CAPACITY,
.memory_event_capacity =
IREE_HAL_VULKAN_LOGICAL_DEVICE_PROFILE_MEMORY_EVENT_CAPACITY,
};
iree_hal_local_profile_recorder_t* recorder = NULL;
iree_status_t status = iree_hal_local_profile_recorder_create(
&recorder_options, &resolved_options, device->host_allocator, &recorder);
iree_allocator_free(device->host_allocator, queue_records);
if (!iree_status_is_ok(status) || !recorder) return status;
device->profile.recorder = recorder;
device->profile.host_time_domain = profile_host_time_domain;
device->profile.next_clock_correlation_sample_id = 0;
status = iree_hal_vulkan_logical_device_write_clock_correlation(device);
if (!iree_status_is_ok(status)) {
device->profile.recorder = NULL;
status =
iree_status_join(status, iree_hal_local_profile_recorder_end(recorder));
iree_hal_local_profile_recorder_destroy(recorder);
return status;
}
iree_atomic_store(&device->profile.next_submission_id, 1,
iree_memory_order_relaxed);
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
iree_hal_vulkan_queue_set_profile_recorder(
&device->queues.lanes[i], recorder,
iree_hal_vulkan_logical_device_profile_queue_scope(
device, iree_hal_vulkan_logical_device_profile_count(i)),
&device->profile.next_submission_id, &device->profile.clock_alignment);
}
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_logical_device_profiling_flush(
iree_hal_device_t* base_device) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
iree_hal_vulkan_queue_drain_completions(&device->queues.lanes[i]);
}
iree_status_t status =
iree_hal_vulkan_logical_device_write_clock_correlation(device);
if (iree_status_is_ok(status)) {
status = iree_hal_local_profile_recorder_flush(device->profile.recorder);
}
return status;
}
static iree_status_t iree_hal_vulkan_logical_device_profiling_end(
iree_hal_device_t* base_device) {
iree_hal_vulkan_logical_device_t* device =
iree_hal_vulkan_logical_device_cast(base_device);
iree_hal_local_profile_recorder_t* recorder = device->profile.recorder;
if (!recorder) return iree_ok_status();
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
iree_hal_vulkan_queue_drain_completions(&device->queues.lanes[i]);
}
iree_status_t status =
iree_hal_vulkan_logical_device_write_clock_correlation(device);
const iree_hal_local_profile_queue_scope_t empty_scope =
iree_hal_local_profile_queue_scope_default();
for (iree_host_size_t i = 0; i < device->queues.lane_count; ++i) {
iree_hal_vulkan_queue_set_profile_recorder(
&device->queues.lanes[i], /*profile_recorder=*/NULL, empty_scope,
/*submission_counter=*/NULL, /*clock_alignment=*/NULL);
}
device->profile.recorder = NULL;
device->profile.host_time_domain = VK_TIME_DOMAIN_DEVICE_EXT;
device->profile.next_clock_correlation_sample_id = 0;
iree_atomic_store(&device->profile.next_submission_id, 0,
iree_memory_order_relaxed);
status =
iree_status_join(status, iree_hal_local_profile_recorder_end(recorder));
iree_hal_local_profile_recorder_destroy(recorder);
return status;
}
static iree_status_t iree_hal_vulkan_logical_device_external_capture_begin(
iree_hal_device_t* base_device,
const iree_hal_device_external_capture_options_t* options) {
(void)base_device;
(void)options;
return iree_hal_vulkan_unimplemented(IREE_SV("external capture"));
}
static iree_status_t iree_hal_vulkan_logical_device_external_capture_end(
iree_hal_device_t* base_device) {
(void)base_device;
return iree_hal_vulkan_unimplemented(IREE_SV("external capture"));
}
static iree_status_t iree_hal_vulkan_logical_device_allocate(
iree_string_view_t identifier, const iree_hal_vulkan_libvulkan_t* libvulkan,
iree_allocator_t host_allocator,
iree_hal_vulkan_logical_device_t** out_device) {
IREE_ASSERT_ARGUMENT(libvulkan);
IREE_ASSERT_ARGUMENT(out_device);
*out_device = NULL;
iree_host_size_t total_size = sizeof(iree_hal_vulkan_logical_device_t);
if (!iree_host_size_checked_add(total_size, identifier.size, &total_size)) {
return iree_make_status(IREE_STATUS_OUT_OF_RANGE,
"Vulkan logical device allocation overflow");
}
iree_hal_vulkan_logical_device_t* device = NULL;
IREE_RETURN_IF_ERROR(
iree_allocator_malloc(host_allocator, total_size, (void**)&device));
memset(device, 0, total_size);
iree_hal_resource_initialize(&iree_hal_vulkan_logical_device_vtable,
&device->resource);
device->host_allocator = host_allocator;
iree_arena_block_pool_initialize(IREE_HAL_VULKAN_COMMAND_BUFFER_BLOCK_SIZE,
host_allocator,
&device->command_buffer_block_pool);
iree_string_view_append_to_buffer(identifier, &device->identifier,
(char*)device + sizeof(*device));
iree_status_t status =
iree_hal_vulkan_libvulkan_copy(libvulkan, &device->libvulkan);
if (iree_status_is_ok(status)) {
iree_slim_mutex_initialize(&device->queues.handle_mutexes.compute);
iree_slim_mutex_initialize(&device->queues.handle_mutexes.transfer);
iree_slim_mutex_initialize(&device->queues.handle_mutexes.sparse_binding);
iree_slim_mutex_initialize(&device->profile.clock_alignment.mutex);
iree_hal_vulkan_profile_clock_alignment_reset(
&device->profile.clock_alignment);
*out_device = device;
} else {
iree_arena_block_pool_deinitialize(&device->command_buffer_block_pool);
iree_allocator_free(host_allocator, device);
}
return status;
}
static iree_status_t iree_hal_vulkan_logical_device_initialize_proactor(
iree_hal_vulkan_logical_device_t* device,
const iree_hal_device_create_params_t* create_params) {
IREE_ASSERT_ARGUMENT(device);
IREE_ASSERT_ARGUMENT(create_params);
device->proactor_pool = create_params->proactor_pool;
iree_async_proactor_pool_retain(device->proactor_pool);
return iree_async_proactor_pool_get(device->proactor_pool, 0,
&device->proactor);
}
static iree_status_t iree_hal_vulkan_logical_device_initialize_allocator(
iree_hal_vulkan_logical_device_t* device) {
IREE_ASSERT_ARGUMENT(device);
return iree_hal_vulkan_allocator_create(
(iree_hal_device_t*)device, &device->syms, device->logical_device,
&device->physical_device, device->enabled_features,
device->enabled_extensions, device->queues.affinity_mask,
device->queues.sparse_binding_lane, device->proactor,
device->host_allocator, &device->device_allocator);
}
static void iree_hal_vulkan_logical_device_resolve_queue_assignment(
iree_hal_vulkan_logical_device_t* device,
const iree_hal_vulkan_queue_assignment_t* queue_assignment) {
device->queues.compute.selection = queue_assignment->compute;
VkDeviceQueueInfo2 queue_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2,
.queueFamilyIndex = device->queues.compute.selection.family_index,
.queueIndex = device->queues.compute.selection.queue_index,
};
iree_vkGetDeviceQueue2(IREE_VULKAN_DEVICE(&device->syms),
device->logical_device, &queue_info,
&device->queues.compute.handle);
device->queues.transfer.selection = queue_assignment->transfer;
queue_info.queueFamilyIndex = device->queues.transfer.selection.family_index;
queue_info.queueIndex = device->queues.transfer.selection.queue_index;
iree_vkGetDeviceQueue2(IREE_VULKAN_DEVICE(&device->syms),
device->logical_device, &queue_info,
&device->queues.transfer.handle);
device->queues.sparse_binding.selection = queue_assignment->sparse_binding;
if (iree_hal_vulkan_queue_assignment_has_sparse_binding(queue_assignment)) {
if (iree_hal_vulkan_queue_selection_is_same(
&device->queues.sparse_binding.selection,
&device->queues.compute.selection)) {
device->queues.sparse_binding.handle = device->queues.compute.handle;
} else if (iree_hal_vulkan_queue_selection_is_same(
&device->queues.sparse_binding.selection,
&device->queues.transfer.selection)) {
device->queues.sparse_binding.handle = device->queues.transfer.handle;
} else {
queue_info.queueFamilyIndex =
device->queues.sparse_binding.selection.family_index;
queue_info.queueIndex =
device->queues.sparse_binding.selection.queue_index;
iree_vkGetDeviceQueue2(IREE_VULKAN_DEVICE(&device->syms),
device->logical_device, &queue_info,
&device->queues.sparse_binding.handle);
}
}
device->queues.count = queue_assignment->queue_count;
device->queues.affinity_mask = (1ull << device->queues.count) - 1;
}
static iree_hal_vulkan_debug_utils_queue_role_flags_t
iree_hal_vulkan_logical_device_queue_debug_roles(
iree_hal_vulkan_logical_device_t* device,
const iree_hal_vulkan_resolved_queue_t* queue) {
iree_hal_vulkan_debug_utils_queue_role_flags_t role_flags =
IREE_HAL_VULKAN_DEBUG_UTILS_QUEUE_ROLE_NONE;
if (iree_hal_vulkan_queue_selection_is_same(
&queue->selection, &device->queues.compute.selection)) {
role_flags |= IREE_HAL_VULKAN_DEBUG_UTILS_QUEUE_ROLE_COMPUTE;
}
if (iree_hal_vulkan_queue_selection_is_same(
&queue->selection, &device->queues.transfer.selection)) {
role_flags |= IREE_HAL_VULKAN_DEBUG_UTILS_QUEUE_ROLE_TRANSFER;
}
if (device->queues.sparse_binding.handle &&
iree_hal_vulkan_queue_selection_is_same(
&queue->selection, &device->queues.sparse_binding.selection)) {
role_flags |= IREE_HAL_VULKAN_DEBUG_UTILS_QUEUE_ROLE_SPARSE_BINDING;
}
return role_flags;
}
static iree_status_t iree_hal_vulkan_logical_device_set_queue_debug_name(
iree_hal_vulkan_logical_device_t* device,
const iree_hal_vulkan_resolved_queue_t* queue) {
const iree_hal_vulkan_debug_utils_queue_role_flags_t role_flags =
iree_hal_vulkan_logical_device_queue_debug_roles(device, queue);
return iree_hal_vulkan_debug_utils_set_queue_name(
&device->debug_utils, &device->syms, device->logical_device,
queue->handle, role_flags, queue->selection.family_index,
queue->selection.queue_index, device->identifier, device->host_allocator);
}
static iree_status_t iree_hal_vulkan_logical_device_set_debug_names(
iree_hal_vulkan_logical_device_t* device) {
IREE_RETURN_IF_ERROR(iree_hal_vulkan_debug_utils_set_object_name(
&device->debug_utils, &device->syms, device->logical_device,
VK_OBJECT_TYPE_DEVICE, (uint64_t)(uintptr_t)device->logical_device,
device->identifier, device->host_allocator));
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_set_queue_debug_name(
device, &device->queues.compute));
if (!iree_hal_vulkan_queue_selection_is_same(
&device->queues.transfer.selection,
&device->queues.compute.selection)) {
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_set_queue_debug_name(
device, &device->queues.transfer));
}
if (device->queues.sparse_binding.handle &&
!iree_hal_vulkan_queue_selection_is_same(
&device->queues.sparse_binding.selection,
&device->queues.compute.selection) &&
!iree_hal_vulkan_queue_selection_is_same(
&device->queues.sparse_binding.selection,
&device->queues.transfer.selection)) {
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_set_queue_debug_name(
device, &device->queues.sparse_binding));
}
return iree_ok_status();
}
static iree_slim_mutex_t* iree_hal_vulkan_logical_device_queue_handle_mutex(
iree_hal_vulkan_logical_device_t* device,
const iree_hal_vulkan_resolved_queue_t* queue) {
if (iree_hal_vulkan_queue_selection_is_same(
&queue->selection, &device->queues.compute.selection)) {
return &device->queues.handle_mutexes.compute;
}
if (iree_hal_vulkan_queue_selection_is_same(
&queue->selection, &device->queues.transfer.selection)) {
return &device->queues.handle_mutexes.transfer;
}
return &device->queues.handle_mutexes.sparse_binding;
}
static iree_status_t iree_hal_vulkan_logical_device_initialize_queue_lane(
iree_hal_vulkan_logical_device_t* device,
const iree_hal_vulkan_resolved_queue_t* queue,
iree_hal_vulkan_queue_role_t role, iree_hal_vulkan_queue_t** out_queue) {
IREE_ASSERT_ARGUMENT(out_queue);
*out_queue = NULL;
if (device->queues.lane_count >= IREE_HAL_VULKAN_MAX_QUEUE_LANES) {
return iree_make_status(IREE_STATUS_RESOURCE_EXHAUSTED,
"Vulkan logical device queue lane storage is full");
}
iree_hal_vulkan_queue_t* queue_lane =
&device->queues.lanes[device->queues.lane_count];
iree_hal_vulkan_queue_params_t params = {
.device = device,
.syms = &device->syms,
.debug_utils = &device->debug_utils,
.logical_device = device->logical_device,
.builtins = &device->builtins,
.enabled_dispatch_abis = device->enabled_dispatch_abis,
.queue = queue->handle,
.queue_flags = queue->selection.flags,
.timestamp_valid_bits = queue->selection.timestamp_valid_bits,
.queue_handle_mutex =
iree_hal_vulkan_logical_device_queue_handle_mutex(device, queue),
.proactor = device->proactor,
.queue_family_index = queue->selection.family_index,
.queue_index = queue->selection.queue_index,
.queue_affinity = queue->selection.affinity,
.role = role,
.host_allocator = device->host_allocator,
.max_cached_bda_replay_instances =
device->max_cached_bda_replay_instances,
.max_cached_bda_replay_publication_bytes =
device->max_cached_bda_replay_publication_bytes,
.retained_cached_bda_replay_instances =
device->retained_cached_bda_replay_instances,
};
IREE_RETURN_IF_ERROR(iree_hal_vulkan_queue_initialize(&params, queue_lane));
device->queues.lane_count = device->queues.lane_count + 1;
*out_queue = queue_lane;
return iree_ok_status();
}
static iree_status_t iree_hal_vulkan_logical_device_initialize_queues(
iree_hal_vulkan_logical_device_t* device) {
IREE_ASSERT_ARGUMENT(device);
device->queues.lane_count = 0;
device->queues.compute_lane = NULL;
device->queues.transfer_lane = NULL;
device->queues.sparse_binding_lane = NULL;
IREE_RETURN_IF_ERROR(iree_hal_vulkan_logical_device_initialize_queue_lane(
device, &device->queues.compute, IREE_HAL_VULKAN_QUEUE_ROLE_COMPUTE,
&device->queues.compute_lane));
iree_status_t status = iree_ok_status();
if (device->queues.transfer.selection.affinity ==
device->queues.compute.selection.affinity) {
device->queues.transfer_lane = device->queues.compute_lane;
} else {
status = iree_hal_vulkan_logical_device_initialize_queue_lane(
device, &device->queues.transfer, IREE_HAL_VULKAN_QUEUE_ROLE_TRANSFER,
&device->queues.transfer_lane);
}
if (iree_status_is_ok(status) && device->queues.sparse_binding.handle) {
if (iree_hal_vulkan_queue_selection_is_same(
&device->queues.sparse_binding.selection,
&device->queues.compute.selection)) {
device->queues.sparse_binding_lane = device->queues.compute_lane;
} else if (iree_hal_vulkan_queue_selection_is_same(
&device->queues.sparse_binding.selection,
&device->queues.transfer.selection)) {
device->queues.sparse_binding_lane = device->queues.transfer_lane;
} else {
status = iree_hal_vulkan_logical_device_initialize_queue_lane(
device, &device->queues.sparse_binding,
IREE_HAL_VULKAN_QUEUE_ROLE_SPARSE_BINDING,
&device->queues.sparse_binding_lane);
}
}
return status;
}
static iree_status_t iree_hal_vulkan_logical_device_initialize_queue_staging(
iree_hal_vulkan_logical_device_t* device) {
IREE_ASSERT_ARGUMENT(device);
IREE_ASSERT_ARGUMENT(device->device_allocator);
iree_status_t status = iree_ok_status();
for (iree_host_size_t i = 0;
i < device->queues.lane_count && iree_status_is_ok(status); ++i) {
status = iree_hal_vulkan_queue_initialize_staging(&device->queues.lanes[i],
device->device_allocator);
}
return status;
}
static iree_status_t iree_hal_vulkan_logical_device_initialize_from_plan(
iree_hal_vulkan_logical_device_t* device,
const iree_hal_vulkan_device_plan_t* device_plan,
const iree_hal_vulkan_device_options_t* device_options,
const iree_hal_device_create_params_t* create_params) {
IREE_ASSERT_ARGUMENT(device);
IREE_ASSERT_ARGUMENT(device_plan);
IREE_ASSERT_ARGUMENT(device_options);
IREE_ASSERT_ARGUMENT(create_params);
iree_status_t status =
iree_hal_vulkan_logical_device_initialize_proactor(device, create_params);
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_libvulkan_load_device_syms(
&device->instance.syms, device->logical_device, &device->syms);
}
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_debug_utils_initialize(
device_plan->request_flags, &device->syms, &device->debug_utils);
}
if (iree_status_is_ok(status)) {
device->enabled_features = device_plan->enabled_features;
device->enabled_extensions = device_plan->enabled_extensions;
device->enabled_dispatch_abis = device_plan->enabled_dispatch_abis;
device->max_cached_bda_replay_instances =
device_options->max_cached_bda_replay_instances;
device->max_cached_bda_replay_publication_bytes =
device_options->max_cached_bda_replay_publication_bytes;
device->retained_cached_bda_replay_instances =
device_options->retained_cached_bda_replay_instances;
}
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_builtins_initialize(
&device->syms, device->logical_device, &device->physical_device,
&device->builtins);
}
if (iree_status_is_ok(status)) {
iree_hal_vulkan_logical_device_resolve_queue_assignment(
device, &device_plan->queue_assignment);
}
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_logical_device_set_debug_names(device);
}
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_logical_device_initialize_queues(device);
}
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_logical_device_initialize_allocator(device);
}
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_logical_device_initialize_queue_staging(device);
}
return status;
}
static iree_status_t iree_hal_vulkan_logical_device_create_from_selection(
iree_string_view_t identifier,
const iree_hal_vulkan_driver_options_t* driver_options,
const iree_hal_vulkan_libvulkan_t* libvulkan,
const iree_hal_vulkan_device_options_t* device_options,
const iree_hal_device_create_params_t* create_params,
iree_allocator_t host_allocator, iree_hal_vulkan_instance_t* instance,
iree_hal_vulkan_physical_device_snapshot_t* snapshot,
iree_hal_device_t** out_device) {
IREE_ASSERT_ARGUMENT(create_params);
IREE_ASSERT_ARGUMENT(out_device);
*out_device = NULL;
IREE_TRACE_ZONE_BEGIN(z0);
iree_hal_vulkan_device_plan_t device_plan;
iree_status_t status = iree_hal_vulkan_device_plan_initialize_for_create(
snapshot, device_options, driver_options->request_flags,
driver_options->requested_features, &device_plan);
iree_hal_vulkan_logical_device_t* device = NULL;
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_logical_device_allocate(identifier, libvulkan,
host_allocator, &device);
}
if (iree_status_is_ok(status)) {
device->instance = *instance;
device->owns_instance = true;
memset(instance, 0, sizeof(*instance));
device->physical_device = *snapshot;
memset(snapshot, 0, sizeof(*snapshot));
}
if (iree_status_is_ok(status)) {
VkDeviceCreateInfo device_create_info;
iree_hal_vulkan_device_plan_make_create_info(&device_plan,
&device_create_info);
status =
iree_vkCreateDevice(IREE_VULKAN_INSTANCE(&device->instance.syms),
device->physical_device.handle, &device_create_info,
/*pAllocator=*/NULL, &device->logical_device);
}
if (iree_status_is_ok(status)) {
device->owns_logical_device = true;
status = iree_hal_vulkan_logical_device_initialize_from_plan(
device, &device_plan, device_options, create_params);
}
if (iree_status_is_ok(status)) {
*out_device = (iree_hal_device_t*)device;
} else if (device) {
iree_hal_device_release((iree_hal_device_t*)device);
}
IREE_TRACE_ZONE_END(z0);
return status;
}
static iree_status_t iree_hal_vulkan_logical_device_create_with_selector(
iree_string_view_t identifier,
const iree_hal_vulkan_driver_options_t* driver_options,
const iree_hal_vulkan_libvulkan_t* libvulkan,
const iree_hal_vulkan_physical_device_selector_t* selector,
iree_host_size_t param_count, const iree_string_pair_t* params,
const iree_hal_device_create_params_t* create_params,
iree_allocator_t host_allocator, iree_hal_device_t** out_device) {
IREE_ASSERT_ARGUMENT(driver_options);
IREE_ASSERT_ARGUMENT(libvulkan);
IREE_ASSERT_ARGUMENT(selector);
IREE_ASSERT_ARGUMENT(create_params);
IREE_ASSERT_ARGUMENT(out_device);
*out_device = NULL;
IREE_TRACE_ZONE_BEGIN(z0);
if (!create_params->proactor_pool) {
IREE_TRACE_ZONE_END(z0);
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"Vulkan logical device creation requires a proactor pool");
}
iree_hal_vulkan_device_options_t device_options =
driver_options->device_options;
IREE_RETURN_AND_END_ZONE_IF_ERROR(
z0, iree_hal_vulkan_device_options_parse(&device_options,
(iree_string_pair_list_t){
.count = param_count,
.pairs = params,
}));
iree_hal_vulkan_instance_t instance;
iree_hal_vulkan_physical_device_snapshot_t snapshot;
iree_status_t status = iree_hal_vulkan_physical_device_select(
libvulkan, driver_options, selector, host_allocator, &instance,
&snapshot);
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_logical_device_create_from_selection(
identifier, driver_options, libvulkan, &device_options, create_params,
host_allocator, &instance, &snapshot, out_device);
}
iree_hal_vulkan_physical_device_snapshot_deinitialize(host_allocator,
&snapshot);
iree_hal_vulkan_instance_deinitialize(&instance);
IREE_TRACE_ZONE_END(z0);
return status;
}
iree_status_t iree_hal_vulkan_logical_device_create_by_id(
iree_string_view_t identifier,
const iree_hal_vulkan_driver_options_t* driver_options,
const iree_hal_vulkan_libvulkan_t* libvulkan,
iree_hal_device_id_t device_id, iree_host_size_t param_count,
const iree_string_pair_t* params,
const iree_hal_device_create_params_t* create_params,
iree_allocator_t host_allocator, iree_hal_device_t** out_device) {
const iree_hal_vulkan_physical_device_selector_t selector = {
.mode = device_id == IREE_HAL_DEVICE_ID_DEFAULT
? IREE_HAL_VULKAN_PHYSICAL_DEVICE_SELECTOR_DEFAULT
: IREE_HAL_VULKAN_PHYSICAL_DEVICE_SELECTOR_ID,
.device_id = device_id,
};
return iree_hal_vulkan_logical_device_create_with_selector(
identifier, driver_options, libvulkan, &selector, param_count, params,
create_params, host_allocator, out_device);
}
iree_status_t iree_hal_vulkan_logical_device_create_by_path(
iree_string_view_t identifier,
const iree_hal_vulkan_driver_options_t* driver_options,
const iree_hal_vulkan_libvulkan_t* libvulkan,
iree_string_view_t device_path, iree_host_size_t param_count,
const iree_string_pair_t* params,
const iree_hal_device_create_params_t* create_params,
iree_allocator_t host_allocator, iree_hal_device_t** out_device) {
const iree_hal_vulkan_physical_device_selector_t selector = {
.mode = iree_string_view_is_empty(device_path)
? IREE_HAL_VULKAN_PHYSICAL_DEVICE_SELECTOR_DEFAULT
: IREE_HAL_VULKAN_PHYSICAL_DEVICE_SELECTOR_PATH,
.device_path = device_path,
};
return iree_hal_vulkan_logical_device_create_with_selector(
identifier, driver_options, libvulkan, &selector, param_count, params,
create_params, host_allocator, out_device);
}
IREE_API_EXPORT iree_status_t iree_hal_vulkan_wrap_device(
iree_string_view_t identifier,
const iree_hal_vulkan_device_options_t* options,
const iree_hal_device_create_params_t* create_params,
const iree_hal_vulkan_syms_t* instance_syms, VkInstance instance,
VkPhysicalDevice physical_device, VkDevice logical_device,
const iree_hal_vulkan_external_device_params_t* external_device_params,
iree_allocator_t host_allocator, iree_hal_device_t** out_device) {
IREE_ASSERT_ARGUMENT(options);
IREE_ASSERT_ARGUMENT(create_params);
IREE_ASSERT_ARGUMENT(instance_syms);
IREE_ASSERT_ARGUMENT(external_device_params);
IREE_ASSERT_ARGUMENT(out_device);
*out_device = NULL;
IREE_TRACE_ZONE_BEGIN(z0);
IREE_RETURN_AND_END_ZONE_IF_ERROR(
z0, iree_hal_vulkan_device_options_verify(options));
if (!instance || !physical_device || !logical_device) {
IREE_TRACE_ZONE_END(z0);
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"external Vulkan wrapping requires non-null instance, physical_device, "
"and logical_device handles");
}
if (!create_params->proactor_pool) {
IREE_TRACE_ZONE_END(z0);
return iree_make_status(
IREE_STATUS_INVALID_ARGUMENT,
"Vulkan logical device creation requires a proactor pool");
}
iree_hal_vulkan_instance_t wrapped_instance = {
.handle = instance,
};
iree_status_t status = iree_hal_vulkan_libvulkan_load_instance_syms(
&instance_syms->libvulkan, instance, &wrapped_instance.syms);
iree_hal_vulkan_physical_device_snapshot_t snapshot;
memset(&snapshot, 0, sizeof(snapshot));
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_physical_device_snapshot_initialize(
&wrapped_instance, physical_device, /*ordinal=*/0, host_allocator,
&snapshot);
}
iree_hal_vulkan_device_plan_t device_plan;
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_device_plan_initialize_for_wrap(
&snapshot, options, external_device_params, &device_plan);
}
iree_hal_vulkan_logical_device_t* device = NULL;
if (iree_status_is_ok(status)) {
status = iree_hal_vulkan_logical_device_allocate(
identifier, &instance_syms->libvulkan, host_allocator, &device);
}
if (iree_status_is_ok(status)) {
device->instance = wrapped_instance;
memset(&wrapped_instance, 0, sizeof(wrapped_instance));
device->physical_device = snapshot;
memset(&snapshot, 0, sizeof(snapshot));
device->logical_device = logical_device;
status = iree_hal_vulkan_logical_device_initialize_from_plan(
device, &device_plan, options, create_params);
}
if (iree_status_is_ok(status)) {
*out_device = (iree_hal_device_t*)device;
} else if (device) {
iree_hal_device_release((iree_hal_device_t*)device);
}
iree_hal_vulkan_physical_device_snapshot_deinitialize(host_allocator,
&snapshot);
IREE_TRACE_ZONE_END(z0);
return status;
}
static const iree_hal_device_vtable_t iree_hal_vulkan_logical_device_vtable = {
.destroy = iree_hal_vulkan_logical_device_destroy,
.id = iree_hal_vulkan_logical_device_id,
.host_allocator = iree_hal_vulkan_logical_device_host_allocator,
.device_allocator = iree_hal_vulkan_logical_device_allocator,
.replace_device_allocator = iree_hal_vulkan_replace_device_allocator,
.replace_channel_provider = iree_hal_vulkan_replace_channel_provider,
.trim = iree_hal_vulkan_logical_device_trim,
.query_i64 = iree_hal_vulkan_logical_device_query_i64,
.query_capabilities = iree_hal_vulkan_logical_device_query_capabilities,
.topology_info = iree_hal_vulkan_logical_device_topology_info,
.refine_topology_edge = iree_hal_vulkan_logical_device_refine_topology_edge,
.assign_topology_info = iree_hal_vulkan_logical_device_assign_topology_info,
.create_channel = iree_hal_vulkan_logical_device_create_channel,
.create_command_buffer =
iree_hal_vulkan_logical_device_create_command_buffer,
.create_event = iree_hal_vulkan_logical_device_create_event,
.create_executable_cache =
iree_hal_vulkan_logical_device_create_executable_cache,
.import_file = iree_hal_vulkan_logical_device_import_file,
.create_semaphore = iree_hal_vulkan_logical_device_create_semaphore,
.query_semaphore_compatibility =
iree_hal_vulkan_logical_device_query_semaphore_compatibility,
.query_queue_pool_backend =
iree_hal_vulkan_logical_device_query_queue_pool_backend,
.queue_alloca = iree_hal_vulkan_logical_device_queue_alloca,
.queue_dealloca = iree_hal_vulkan_logical_device_queue_dealloca,
.queue_fill = iree_hal_vulkan_logical_device_queue_fill,
.queue_update = iree_hal_vulkan_logical_device_queue_update,
.queue_copy = iree_hal_vulkan_logical_device_queue_copy,
.queue_read = iree_hal_vulkan_logical_device_queue_read,
.queue_write = iree_hal_vulkan_logical_device_queue_write,
.queue_host_call = iree_hal_vulkan_logical_device_queue_host_call,
.queue_dispatch = iree_hal_vulkan_logical_device_queue_dispatch,
.queue_execute = iree_hal_vulkan_logical_device_queue_execute,
.queue_flush = iree_hal_vulkan_logical_device_queue_flush,
.profiling_begin = iree_hal_vulkan_logical_device_profiling_begin,
.profiling_flush = iree_hal_vulkan_logical_device_profiling_flush,
.profiling_end = iree_hal_vulkan_logical_device_profiling_end,
.external_capture_begin =
iree_hal_vulkan_logical_device_external_capture_begin,
.external_capture_end = iree_hal_vulkan_logical_device_external_capture_end,
};