stride3d/stride · critical · InvalidOperationException

No suitable memory type

Error message

No suitable memory type

What it means

Thrown by FindMemoryType when scanning the physical device's memory properties and no memory type both satisfies the required type bits and carries all required property flags. Vulkan memory types are device-specific, so a combination like HOST_VISIBLE|HOST_COHERENT|DEVICE_LOCAL may simply not exist on a given GPU.

Solutions

  1. Relax the required property flags: for a host upload buffer, HOST_VISIBLE|HOST_COHERENT is usually enough — drop DEVICE_LOCAL from the requirement.
  2. Two-step allocation: create a DEVICE_LOCAL device buffer and a separate HOST_VISIBLE staging buffer, then copy with vkCmdCopyBuffer.
  3. Add a fallback pass in FindMemoryType that retries with a reduced `required` mask and logs the downgrade.
  4. Dump memoryProperties (types and heaps) to confirm which property combinations the device actually supports.

Example fix

// before
var (buffer, memory) = CreateHostBuffer(size, usage, data, required: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
// after
var (buffer, memory) = CreateHostBuffer(size, usage, data, required: VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
Defensive patterns

Strategy: fallback

Validate before calling

static bool HasMemoryType(VkPhysicalDeviceMemoryProperties props, uint typeBits, VkMemoryPropertyFlags required)
{
    for (uint i = 0; i < props.memoryTypeCount; i++)
        if ((typeBits & (1u << (int)i)) != 0 && (props.memoryTypes[i].propertyFlags & required) == required)
            return true;
    return false;
}

Try / catch

try
{
    AllocateBuffer(size, usage, required);
}
catch (InvalidOperationException ex) when (ex.Message == "No suitable memory type")
{
    AllocateBuffer(size, usage, required & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // relax and retry
}

Prevention

When it happens

Trigger: Calling CreateHostBuffer/AllocateBuffer which computes a memoryTypeIndex from vkGetPhysicalDeviceMemoryProperties with (typeBits & (1<<i)) != 0 && (propertyFlags & required) == required; no type matches, so the loop falls through to the throw.

Common situations: Requesting DEVICE_LOCAL plus HOST_VISIBLE staging memory on devices with small BAR (no ReBAR); asking for properties no available type offers (e.g. HOST_CACHED on some iGPUs); passing wrong typeBits from vkGetBufferMemoryRequirements so allowed types exclude the desired properties.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

            queueCreateInfoCount = 1,
            pQueueCreateInfos = &queueCreateInfo,
            enabledExtensionCount = ppEnabledExtensionNames.Length,
            ppEnabledExtensionNames = ppEnabledExtensionNames,
        };
        Check(instanceApi.vkCreateDevice(physicalDevice, in createInfo, null, out device));
        deviceApi = new VkDeviceApi(instanceApi, in device);
        deviceApi.vkGetDeviceQueue(device, queueFamily, 0, out queue);
    }

    private static uint FindMemoryType(uint typeBits, VkMemoryPropertyFlags required)
    {
        instanceApi.vkGetPhysicalDeviceMemoryProperties(physicalDevice, out var memoryProperties);
        for (uint i = 0; i < memoryProperties.memoryTypeCount; i++)
        {
            if ((typeBits & (1u << (int)i)) != 0 && (memoryProperties.memoryTypes[(int)i].propertyFlags & required) == required)
                return i;
        }
        throw new InvalidOperationException("No suitable memory type");
    }

    private (VkBuffer Buffer, VkDeviceMemory Memory) CreateHostBuffer(ulong size, VkBufferUsageFlags usage, ReadOnlySpan<byte> data = default)
    {
        var createInfo = new VkBufferCreateInfo
        {
            sType = VkStructureType.BufferCreateInfo,
            size = size,
            usage = usage,
            sharingMode = VkSharingMode.Exclusive,
        };
        Check(deviceApi.vkCreateBuffer(device, &createInfo, null, out var buffer));
        deviceApi.vkGetBufferMemoryRequirements(device, buffer, out var requirements);
        var allocateInfo = new VkMemoryAllocateInfo
        {
            sType = VkStructureType.MemoryAllocateInfo,
            allocationSize = requirements.size,
            memoryTypeIndex = FindMemoryType(requirements.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent),

View on GitHub (pinned to 96fad776d2)