Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 100 additions & 6 deletions backends/arm/runtime/VGFBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,13 @@ VkResult vkml_allocate_basics(
VkCommandPool* command_pool,
uint32_t* queue_family_index,
bool request_neural_statistics,
bool* neural_statistics_device_enabled);
bool* neural_statistics_device_enabled,
bool request_host_memory_import,
VgfHostMemoryImportCapabilities* host_memory_import_capabilities);

// Helper functions to dump VGF Delegate Boundary Inputs
constexpr const char* kVgfHostMemoryImportEnableEnv =
"EXECUTORCH_VGF_ENABLE_HOST_MEMORY_IMPORT";
constexpr const char* kVgfDumpInputsDirEnv = "EXECUTORCH_VGF_DUMP_INPUTS_DIR";
constexpr const char* kVgfDumpInputsAndExitEnv =
"EXECUTORCH_VGF_DUMP_INPUTS_AND_EXIT";
Expand Down Expand Up @@ -418,8 +422,11 @@ class VGFBackend final : public ::executorch::runtime::BackendInterface {

VkResult result;
neural_statistics_config_ = get_vgf_neural_statistics_runtime_config();
const bool request_host_memory_import =
env_flag_enabled(kVgfHostMemoryImportEnableEnv);

// Fetch basic vulkan objects once
// Fetch basic Vulkan objects once. Device extensions must be selected here,
// before vkCreateDevice; VgfRepr only receives already-created handles.
result = vkml_allocate_basics(
&vk_instance,
&vk_physical_device,
Expand All @@ -428,7 +435,9 @@ class VGFBackend final : public ::executorch::runtime::BackendInterface {
&vk_command_pool,
&vk_queue_family_index,
neural_statistics_config_.requested,
&neural_statistics_device_enabled_);
&neural_statistics_device_enabled_,
request_host_memory_import,
&host_memory_import_capabilities_);
if (result != VK_SUCCESS) {
ET_LOG(
Error, "Failed to initialize the Vulkan device error 0x%08X", result);
Expand Down Expand Up @@ -535,7 +544,8 @@ class VGFBackend final : public ::executorch::runtime::BackendInterface {
vk_queue_family_index,
neural_statistics_config_.requested,
neural_statistics_device_enabled_,
neural_statistics_config_.mode_index);
neural_statistics_config_.mode_index,
host_memory_import_capabilities_);

#ifdef ET_EVENT_TRACER_ENABLED
event_tracer_end_profiling_delegate(event_tracer, allocate_repr_event);
Expand Down Expand Up @@ -865,6 +875,7 @@ class VGFBackend final : public ::executorch::runtime::BackendInterface {
vk_queue_family_index = UINT32_MAX;
neural_statistics_config_ = {};
neural_statistics_device_enabled_ = false;
host_memory_import_capabilities_ = {};
is_initialized_ = false;
// Do not call volkFinalize(): the Vulkan backend shares the loader.
}
Expand All @@ -879,6 +890,7 @@ class VGFBackend final : public ::executorch::runtime::BackendInterface {
uint32_t vk_queue_family_index = UINT32_MAX;
VgfNeuralStatisticsRuntimeConfig neural_statistics_config_{};
bool neural_statistics_device_enabled_ = false;
VgfHostMemoryImportCapabilities host_memory_import_capabilities_{};
bool is_initialized_ = false;
};

Expand All @@ -896,12 +908,17 @@ VkResult vkml_allocate_basics(
VkCommandPool* command_pool,
uint32_t* queue_family_index,
bool request_neural_statistics,
bool* neural_statistics_device_enabled) {
bool* neural_statistics_device_enabled,
bool request_host_memory_import,
VgfHostMemoryImportCapabilities* host_memory_import_capabilities) {
VkResult result;

if (neural_statistics_device_enabled != nullptr) {
*neural_statistics_device_enabled = false;
}
if (host_memory_import_capabilities != nullptr) {
*host_memory_import_capabilities = {};
}

if (VK_SUCCESS != volkInitialize()) {
ET_LOG(Error, "Volk failed to initialize");
Expand Down Expand Up @@ -1156,6 +1173,59 @@ VkResult vkml_allocate_basics(

vector<const char*> requested_exts;

bool enable_host_memory_import_device = false;
#if defined(VK_EXT_external_memory_host)
const bool host_memory_import_advertised = std::any_of(
available.begin(), available.end(), [](const auto& ext_avail) {
return std::strcmp(
VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
ext_avail.extensionName) == 0;
});

VkDeviceSize min_imported_host_pointer_alignment = 0;
if (host_memory_import_advertised) {
VkPhysicalDeviceExternalMemoryHostPropertiesEXT host_memory_properties{
.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT,
.pNext = nullptr,
};
VkPhysicalDeviceProperties2 properties_2{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &host_memory_properties,
};
vkGetPhysicalDeviceProperties2(*physical_device, &properties_2);
min_imported_host_pointer_alignment =
host_memory_properties.minImportedHostPointerAlignment;
}

if (host_memory_import_capabilities != nullptr) {
host_memory_import_capabilities->physical_device_advertised =
host_memory_import_advertised;
host_memory_import_capabilities->min_imported_host_pointer_alignment =
min_imported_host_pointer_alignment;
}

enable_host_memory_import_device = vgf_host_memory_import_should_be_enabled(
request_host_memory_import, host_memory_import_advertised);
if (enable_host_memory_import_device) {
requested_exts.push_back(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
} else if (request_host_memory_import) {
ET_LOG(
Info,
"%s was requested but the Vulkan physical device does not expose %s",
kVgfHostMemoryImportEnableEnv,
VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
}
#else
if (request_host_memory_import) {
ET_LOG(
Info,
"%s was requested but Vulkan headers do not expose "
"VK_EXT_external_memory_host",
kVgfHostMemoryImportEnableEnv);
}
#endif

const bool bfloat16_extension_available = std::any_of(
available.begin(), available.end(), [](const auto& ext_avail) {
return std::strcmp(
Expand Down Expand Up @@ -1263,12 +1333,36 @@ VkResult vkml_allocate_basics(
// Load the device with volk and populate function pointers
volkLoadDevice(*device);

if (host_memory_import_capabilities != nullptr) {
// Only a successful vkCreateDevice makes extension enablement
// authoritative. Physical-device advertisement alone is insufficient for
// later zero-copy code to use VK_EXT_external_memory_host device
// functionality.
host_memory_import_capabilities->logical_device_enabled =
enable_host_memory_import_device;
ET_LOG(
Info,
"VGF host memory import: requested=%d advertised=%d enabled=%d "
"minImportedHostPointerAlignment=%llu",
static_cast<int>(request_host_memory_import),
static_cast<int>(
host_memory_import_capabilities->physical_device_advertised),
static_cast<int>(
host_memory_import_capabilities->logical_device_enabled),
static_cast<unsigned long long>(
host_memory_import_capabilities
->min_imported_host_pointer_alignment));
}

vkGetDeviceQueue(*device, qf, 0, queue);

VkCommandPoolCreateInfo poolInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
// VGF records a persistent per-repr command buffer. Host-memory import
// may later rebind descriptors and re-record only that command buffer.
// Keep the legacy flags=0 behavior unless the extension is truly enabled.
.flags = vgf_command_pool_flags(enable_host_memory_import_device),
.queueFamilyIndex = qf,
};
result = vkCreateCommandPool(*device, &poolInfo, nullptr, command_pool);
Expand Down
25 changes: 23 additions & 2 deletions backends/arm/runtime/VGFSetup.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ using executorch::runtime::CompileSpec;
#include <executorch/backends/vulkan/runtime/vk_api/vk_api.h>

#include <executorch/backends/arm/runtime/VGFNeuralStatistics.h>
#include <executorch/backends/arm/runtime/VGFVulkanFeatures.h>

namespace executorch {
namespace backends {
Expand Down Expand Up @@ -108,7 +109,8 @@ class VgfRepr {
uint32_t queue_family_index = UINT32_MAX,
bool neural_statistics_requested = false,
bool neural_statistics_device_enabled = false,
int neural_statistics_mode_index = 1)
int neural_statistics_mode_index = 1,
VgfHostMemoryImportCapabilities host_memory_import_capabilities = {})
: vk_instance(inst),
vk_physical(phys),
vk_device(dev),
Expand All @@ -117,7 +119,8 @@ class VgfRepr {
vk_queue_family_index(queue_family_index),
neural_statistics_requested_(neural_statistics_requested),
neural_statistics_device_enabled_(neural_statistics_device_enabled),
neural_statistics_mode_index_(neural_statistics_mode_index) {}
neural_statistics_mode_index_(neural_statistics_mode_index),
host_memory_import_capabilities_(host_memory_import_capabilities) {}

/*
* Process a VGF ready for execution, allocate necessary Vulkan objects.
Expand Down Expand Up @@ -175,6 +178,23 @@ class VgfRepr {
return neural_statistics_requested_;
}

bool host_memory_import_advertised() const {
return host_memory_import_capabilities_.physical_device_advertised;
}

bool host_memory_import_enabled() const {
return host_memory_import_capabilities_.logical_device_enabled;
}

VkDeviceSize min_imported_host_pointer_alignment() const {
return host_memory_import_capabilities_.min_imported_host_pointer_alignment;
}

const VgfHostMemoryImportCapabilities& host_memory_import_capabilities()
const {
return host_memory_import_capabilities_;
}

~VgfRepr() {
free_vgf();
if (vk_pipeline_cache != VK_NULL_HANDLE) {
Expand Down Expand Up @@ -203,6 +223,7 @@ class VgfRepr {
bool neural_statistics_requested_ = false;
bool neural_statistics_device_enabled_ = false;
int neural_statistics_mode_index_ = 1;
VgfHostMemoryImportCapabilities host_memory_import_capabilities_{};

bool timestamp_queries_enabled = false;
uint32_t timestamp_valid_bits = 0;
Expand Down
23 changes: 23 additions & 0 deletions backends/arm/runtime/VGFVulkanFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@ namespace executorch {
namespace backends {
namespace vgf {

// Snapshot of VK_EXT_external_memory_host state for the exact Vulkan device
// used by VGF. Physical-device advertisement and logical-device enablement are
// deliberately separate because Vulkan does not provide a post-creation query
// for the list of extensions enabled at vkCreateDevice().
struct VgfHostMemoryImportCapabilities {
bool physical_device_advertised = false;
bool logical_device_enabled = false;
VkDeviceSize min_imported_host_pointer_alignment = 0;
};

inline bool vgf_host_memory_import_should_be_enabled(
bool requested,
bool physical_device_advertised) {
return requested && physical_device_advertised;
}

inline VkCommandPoolCreateFlags vgf_command_pool_flags(
bool host_memory_import_enabled) {
return host_memory_import_enabled
? VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
: 0;
}

inline VkPhysicalDeviceDataGraphFeaturesARM make_vgf_data_graph_features(
void* p_next) {
VkPhysicalDeviceDataGraphFeaturesARM features{};
Expand Down
54 changes: 54 additions & 0 deletions backends/arm/test/vgf_vulkan_features_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,60 @@ TEST(VgfVulkanFeaturesTest, RequiresDataGraphShaderModuleSupport) {
EXPECT_TRUE(vgf_data_graph_features_supported(available));
}

TEST(VgfVulkanFeaturesTest, EnablesHostMemoryImportWhenRequestedAndAdvertised) {
EXPECT_TRUE(vgf_host_memory_import_should_be_enabled(
/*requested=*/true, /*physical_device_advertised=*/true));
}

TEST(
VgfVulkanFeaturesTest,
DoesNotEnableHostMemoryImportWhenAdvertisedButNotRequested) {
EXPECT_FALSE(vgf_host_memory_import_should_be_enabled(
/*requested=*/false, /*physical_device_advertised=*/true));
}

TEST(
VgfVulkanFeaturesTest,
DoesNotEnableHostMemoryImportWhenRequestedButNotAdvertised) {
EXPECT_FALSE(vgf_host_memory_import_should_be_enabled(
/*requested=*/true, /*physical_device_advertised=*/false));
}

TEST(VgfVulkanFeaturesTest, LegacyHostMemoryImportPathRemainsDisabled) {
EXPECT_FALSE(vgf_host_memory_import_should_be_enabled(
/*requested=*/false, /*physical_device_advertised=*/false));
}

TEST(VgfVulkanFeaturesTest, HostImportCommandPoolSupportsIndividualReset) {
EXPECT_EQ(
vgf_command_pool_flags(/*host_memory_import_enabled=*/true),
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
}

TEST(VgfVulkanFeaturesTest, LegacyCommandPoolFlagsRemainUnchanged) {
EXPECT_EQ(vgf_command_pool_flags(/*host_memory_import_enabled=*/false), 0u);
}

TEST(VgfVulkanFeaturesTest, HostMemoryImportCapabilitiesDefaultToUnavailable) {
const VgfHostMemoryImportCapabilities capabilities{};
EXPECT_FALSE(capabilities.physical_device_advertised);
EXPECT_FALSE(capabilities.logical_device_enabled);
EXPECT_EQ(capabilities.min_imported_host_pointer_alignment, 0u);
}

TEST(
VgfVulkanFeaturesTest,
HostMemoryImportCapabilitiesDistinguishAdvertisedFromEnabled) {
VgfHostMemoryImportCapabilities capabilities{};
capabilities.physical_device_advertised = true;
capabilities.logical_device_enabled = false;
capabilities.min_imported_host_pointer_alignment = 4096;

EXPECT_TRUE(capabilities.physical_device_advertised);
EXPECT_FALSE(capabilities.logical_device_enabled);
EXPECT_EQ(capabilities.min_imported_host_pointer_alignment, 4096u);
}

} // namespace
} // namespace vgf
} // namespace backends
Expand Down
Loading