stride3d/stride · critical · InvalidOperationException

No graphics+compute queue family

Error message

No graphics+compute queue family

What it means

Thrown by CreateDevice during Vulkan physical-device setup when no queue family supports both graphics and compute operations. The renderer requires a single queue family capable of both (it probes vkGetPhysicalDeviceQueueFamilyProperties for combined GRAPHICS|COMPUTE flag bits) and refuses to create a logical device without one. This keeps the pipeline simple by avoiding separate graphics and compute queues.

Solutions

  1. Select a different physicalDevice that exposes a graphics+compute queue family (iterate vkEnumeratePhysicalDevices and probe each one's queue families before creating the device).
  2. If separate families must be used, extend the probe to allow graphics-only and compute-only families and create two queues instead of one combined queue.
  3. Verify with vkinfo/vulkaninfo that the intended device actually has a queue family with both GRAPHICS and COMPUTE caps; update drivers if a capable device exists but reports none.

Example fix

// before
var physicalDevice = physicalDevices.First();
CreateDevice(physicalDevice);
// after
var physicalDevice = physicalDevices.FirstOrDefault(CapableOfGraphicsCompute)
    ?? throw new InvalidOperationException("No device with graphics+compute queue family");
CreateDevice(physicalDevice);

static bool CapableOfGraphicsCompute(PhysicalDevice d) { /* probe queue families */ }
Defensive patterns

Strategy: validation

Validate before calling

uint FindGraphicsComputeFamily(PhysicalDevice physical, GetQueueFamilyPropertiesFn getProps)
{
    getProps(physical, out uint count, null);
    var fams = new VkQueueFamilyProperties[count];
    getProps(physical, out _, fams);
    for (uint i = 0; i < count; i++)
        if ((fams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0 && (fams[i].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0)
            return i;
    return uint.MaxValue; // check before CreateDevice and pick another physical device
}

Prevention

When it happens

Trigger: Calling EnsureSharedContext/CreateDevice on a device whose vkGetPhysicalDeviceQueueFamilyProperties returns no family with both VK_QUEUE_GRAPHICS_BIT and VK_QUEUE_COMPUTE_BIT set, so the loop ends with queueFamily still == uint.MaxValue.

Common situations: Running on a compute-only device (e.g. a headless NVIDIA compute GPU, Intel Xe compute-only iGPU) or a minimal display-only device; picking the wrong physicalDevice among multiple devices in a multi-GPU system; running in an emulator or software rasterizer (llvmpipe variants) that exposes no compute support.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Tests/FrameRenderer.Vulkan.cs:199

    private static void CreateDevice()
    {
        uint familyCount = 0;
        instanceApi.vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, null);
        var families = new VkQueueFamilyProperties[familyCount];
        fixed (VkQueueFamilyProperties* familiesPtr = families)
            instanceApi.vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, familiesPtr);
        queueFamily = uint.MaxValue;
        for (uint i = 0; i < familyCount; i++)
        {
            if ((families[i].queueFlags & (VkQueueFlags.Graphics | VkQueueFlags.Compute)) == (VkQueueFlags.Graphics | VkQueueFlags.Compute))
            {
                queueFamily = i;
                break;
            }
        }
        if (queueFamily == uint.MaxValue)
            throw new InvalidOperationException("No graphics+compute queue family");

        // The bytecode keeps UserSemantic decorations (SPV_GOOGLE_hlsl_functionality1);
        // enable the matching extension where the driver wants it declared.
        uint extensionCount = 0;
        Check(instanceApi.vkEnumerateDeviceExtensionProperties(physicalDevice, null, &extensionCount, null));
        var extensions = new VkExtensionProperties[extensionCount];
        fixed (VkExtensionProperties* extensionsPtr = extensions)
            Check(instanceApi.vkEnumerateDeviceExtensionProperties(physicalDevice, null, &extensionCount, extensionsPtr));
        var availableExtensions = new HashSet<string>();
        for (int i = 0; i < extensions.Length; i++)
        {
            fixed (VkExtensionProperties* extensionPtr = &extensions[i])
                availableExtensions.Add(Marshal.PtrToStringUTF8((nint)extensionPtr->extensionName));
        }
        var enabledExtensions = new List<string>();
        foreach (var wanted in new[] { "VK_GOOGLE_hlsl_functionality1", "VK_GOOGLE_user_type" })
        {
            if (availableExtensions.Contains(wanted))

View on GitHub (pinned to 96fad776d2)