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
- Relax the required property flags: for a host upload buffer, HOST_VISIBLE|HOST_COHERENT is usually enough — drop DEVICE_LOCAL from the requirement.
- Two-step allocation: create a DEVICE_LOCAL device buffer and a separate HOST_VISIBLE staging buffer, then copy with vkCmdCopyBuffer.
- Add a fallback pass in FindMemoryType that retries with a reduced `required` mask and logs the downgrade.
- 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
- Never require DEVICE_LOCAL together with HOST_VISIBLE unless you've verified ReBAR/large-BAR support.
- Cache the device's memory properties once and consult them when choosing flags.
- Design allocations as staging+device-local pairs instead of one buffer with all properties.
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
- AHardwareBuffer import requires…
- argumentsBuffer
- Bound render targets
- failed
- Cannot copy data between Buffers and Textures.
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)