Skip to content

Commit c6b587e

Browse files
SS-JIAclaude
andcommitted
Fix Vulkan subgroup size control crash by targeting Vulkan 1.3 core
Summary: Fixes a native Vulkan compute pipeline creation crash (-3 VK_ERROR_INITIALIZATION_FAILED) on Android 16 (API 36) drivers. VK_EXT_subgroup_size_control was promoted to core in Vulkan 1.3, so those drivers no longer advertise it as a separate device extension. ExecuTorch targeted a Vulkan 1.1 instance, where the feature is neither core nor available as an extension, yet the physical device still reported it as supported. Chaining VkPhysicalDeviceSubgroupSizeControlFeaturesEXT into VkDeviceCreateInfo.pNext without a valid enable route is invalid usage and crashes device/pipeline creation. This raises the requested instance apiVersion to Vulkan 1.3 (capped at the loader-supported version, falling back to 1.1) so the feature can be used via the core path, and gates subgroup size control per device: it is enabled only when the extension is enabled, or when both the instance and the physical device are at 1.3+. Otherwise the related capability flags are cleared so downstream shader dispatch does not request an unsupported subgroup size. On Android 16 devices this restores subgroup size control via the core route rather than disabling it. Authored with Claude. Fixes #11754. Test Plan: Export and run a Vulkan-delegated model on an Android 16 (API 36) device and confirm compute pipelines create successfully. Also verify on a Vulkan 1.1 device that the feature falls back cleanly without crashing. Reviewers: Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b1dd46a commit c6b587e

4 files changed

Lines changed: 64 additions & 7 deletions

File tree

backends/vulkan/runtime/vk_api/Adapter.cpp

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <executorch/backends/vulkan/runtime/vk_api/Adapter.h>
1212

13+
#include <cstring>
1314
#include <iomanip>
1415
#include <sstream>
1516

@@ -83,7 +84,7 @@ void populate_queue_info(
8384
}
8485

8586
VkDevice create_logical_device(
86-
const PhysicalDevice& physical_device,
87+
PhysicalDevice& physical_device,
8788
const uint32_t num_queues_to_create,
8889
std::vector<Adapter::Queue>& queues,
8990
std::vector<uint32_t>& queue_usage) {
@@ -217,15 +218,46 @@ VkDevice create_logical_device(
217218
#endif /* VK_NV_cooperative_matrix2 */
218219

219220
#ifdef VK_EXT_subgroup_size_control
220-
// Only enable the feature struct if the extension was actually requested
221-
// and the feature flag is set on the physical device. The extension itself
222-
// is filtered into enabled_device_extensions by
223-
// find_requested_device_extensions.
221+
// The subgroup size control feature struct may only be chained into
222+
// VkDeviceCreateInfo.pNext if the feature is actually going to be enabled on
223+
// the logical device. It can be enabled via two routes:
224+
// 1. The VK_EXT_subgroup_size_control extension is enabled, or
225+
// 2. It is available as core (promoted to core in Vulkan 1.3), which
226+
// requires both the instance and the physical device to be at 1.3+.
227+
// On Vulkan 1.3-core drivers the extension is frequently not advertised as a
228+
// separate string, so checking enabled_device_extensions alone is not
229+
// sufficient. Chaining the struct without either route being available is
230+
// invalid usage and crashes device/pipeline creation with
231+
// VK_ERROR_INITIALIZATION_FAILED.
232+
bool subgroup_size_control_extension_enabled = false;
233+
for (const auto& ext : enabled_device_extensions) {
234+
if (strcmp(ext, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME) == 0) {
235+
subgroup_size_control_extension_enabled = true;
236+
break;
237+
}
238+
}
239+
240+
bool subgroup_size_control_core_available = false;
241+
#if defined(VK_VERSION_1_3)
242+
const bool device_supports_1_3 = physical_device.api_version_major > 1 ||
243+
(physical_device.api_version_major == 1 &&
244+
physical_device.api_version_minor >= 3);
245+
const bool instance_supports_1_3 =
246+
select_instance_api_version() >= VK_API_VERSION_1_3;
247+
subgroup_size_control_core_available =
248+
device_supports_1_3 && instance_supports_1_3;
249+
#endif /* VK_VERSION_1_3 */
250+
224251
VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroup_size_control_features{
225252
physical_device.subgroup_size_control_features};
226-
if (physical_device.supports_subgroup_size_control) {
253+
if (physical_device.supports_subgroup_size_control &&
254+
(subgroup_size_control_extension_enabled ||
255+
subgroup_size_control_core_available)) {
227256
subgroup_size_control_features.pNext = extension_list_top;
228257
extension_list_top = &subgroup_size_control_features;
258+
} else {
259+
physical_device.supports_subgroup_size_control = false;
260+
physical_device.supports_compute_full_subgroups = false;
229261
}
230262
#endif /* VK_EXT_subgroup_size_control */
231263

backends/vulkan/runtime/vk_api/Device.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,24 @@ DeviceType determine_device_type(const std::string& device_name) {
4040

4141
} // namespace
4242

43+
uint32_t select_instance_api_version() {
44+
uint32_t requested_version = VK_API_VERSION_1_1;
45+
#if defined(VK_VERSION_1_3)
46+
auto enumerate_instance_version =
47+
(PFN_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(
48+
VK_NULL_HANDLE, "vkEnumerateInstanceVersion");
49+
if (enumerate_instance_version != nullptr) {
50+
uint32_t loader_version = 0;
51+
if (enumerate_instance_version(&loader_version) == VK_SUCCESS) {
52+
requested_version = loader_version < VK_API_VERSION_1_3
53+
? loader_version
54+
: VK_API_VERSION_1_3;
55+
}
56+
}
57+
#endif /* VK_VERSION_1_3 */
58+
return requested_version;
59+
}
60+
4361
PhysicalDevice::PhysicalDevice(
4462
VkInstance instance_handle,
4563
VkPhysicalDevice physical_device_handle)

backends/vulkan/runtime/vk_api/Device.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ struct DeviceHandle final {
118118
~DeviceHandle();
119119
};
120120

121+
// Returns the Vulkan instance apiVersion that ExecuTorch should request. Aims
122+
// for Vulkan 1.3 (which exposes VK_EXT_subgroup_size_control as core, so it can
123+
// be used without a separately advertised extension) but never exceeds the
124+
// version the loader supports. Falls back to 1.1 on loaders that predate
125+
// vkEnumerateInstanceVersion.
126+
uint32_t select_instance_api_version();
127+
121128
void find_requested_device_extensions(
122129
VkPhysicalDevice physical_device,
123130
std::vector<const char*>& enabled_extensions,

backends/vulkan/runtime/vk_api/Runtime.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ VkInstance create_instance(const RuntimeConfig& config) {
8787
0, // applicationVersion
8888
nullptr, // pEngineName
8989
0, // engineVersion
90-
VK_API_VERSION_1_1, // apiVersion
90+
select_instance_api_version(), // apiVersion
9191
};
9292

9393
std::vector<const char*> enabled_layers;

0 commit comments

Comments
 (0)