stride3d/stride · critical · NotSupportedException

Required Vulkan extension

Error message

Required Vulkan extension {extensionName} is not supported by the current physical device.

What it means

Before creating a Vulkan logical device, Stride validates that the chosen physical device exposes the VK_KHR_swapchain extension, which is mandatory for presenting rendered images to a window surface. ValidateExtensionPropertiesAvailability throws NotSupportedException if the device's enumerated extension list does not contain it, since no swapchain can exist without it.

Solutions

  1. Verify with vulkaninfo that the selected physical device lists VK_KHR_swapchain under device extensions.
  2. Install/repair full vendor GPU drivers rather than a headless or compute-only ICD.
  3. Run in headless/offscreen mode without a swapchain if presentation is not needed.
  4. Select a different, presentation-capable physical device in the adapter list.
Defensive patterns

Strategy: validation

Validate before calling

bool hasSwapchain = physicalDevice.EnumerateDeviceExtensionProperties()
    .Any(e => e.ExtensionName == "VK_KHR_swapchain");
if (!hasSwapchain) throw new NotSupportedException("Device lacks VK_KHR_swapchain");

Type guard

bool SupportsSwapchain(PhysicalDevice d) => d.EnumerateDeviceExtensionProperties().Any(e => e.ExtensionName == "VK_KHR_swapchain");

Try / catch

try { device = GraphicsDevice.New(adapter, desc); }
catch (NotSupportedException ex) when (ex.Message.Contains("VK_KHR_swapchain")) { log.Error("Presentation unsupported on this device", ex); throw; }

Prevention

When it happens

Trigger: GraphicsDevice creation with a swapchain (windowed mode) on a physical device that fails to enumerate VK_KHR_swapchain in its device extension properties.

Common situations: Headless Vulkan ICDs or compute-only devices (some server GPUs, lavapipe variants) that lack presentation support; misconfigured Vulkan runtime/loader; a device selected without swapchain capability (e.g. picking device index 0 which is a compute accelerator).

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/73858410de60d2c1. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs:672

            {
                var properties = extensionProperties[index];
                var name = new VkUtf8String(properties.extensionName);
                var indexOfExtensionName = supportedExtensionProperties.IndexOf(name);

                if (indexOfExtensionName >= 0)
                    availableExtensionProperties.Add(supportedExtensionProperties[indexOfExtensionName]);
            }

            return availableExtensionProperties;
        }

        private static void ValidateExtensionPropertiesAvailability(HashSet<VkUtf8String> availableExtensionProperties)
        {
            if (!availableExtensionProperties.Contains(VK_KHR_SWAPCHAIN_EXTENSION_NAME))
            {
                string extensionName = Encoding.UTF8.GetString(VK_KHR_SWAPCHAIN_EXTENSION_NAME);

                throw new NotSupportedException($"Required Vulkan extension {extensionName} is not supported by the current physical device.");
            }
        }

        internal unsafe IntPtr AllocateUploadBuffer(int size, out VkBuffer resource, out int offset)
        {
            lock (nativeUploadBufferLock)
            {
                if (nativeUploadBuffer == VkBuffer.Null || nativeUploadBufferOffset + size > nativeUploadBufferSize)
                {
                    if (nativeUploadBuffer != VkBuffer.Null)
                    {
                        NativeDeviceApi.vkUnmapMemory(NativeDevice, nativeUploadBufferMemory);
                        Collect(nativeUploadBuffer);
                        Collect(nativeUploadBufferMemory);
                    }

                    // Allocate new buffer
                    // TODO D3D12 recycle old ones (using fences to know when GPU is done with them)

View on GitHub (pinned to 96fad776d2)