stride3d/stride · error · InvalidOperationException

IOSurface import requires VK_EXT_metal_objects.

Error message

IOSurface import requires VK_EXT_metal_objects.

What it means

Texture.NewFromIOSurface imports an Apple IOSurface as a Vulkan texture on macOS/iOS, which requires the VK_EXT_metal_objects extension (device.HasMetalObjectsSupport). If the Vulkan implementation (e.g. MoltenVK) does not report support, Stride throws InvalidOperationException instead of attempting the import.

Solutions

  1. Check device.HasMetalObjectsSupport before calling and fall back to uploading texture data normally when it is false.
  2. Upgrade MoltenVK to a version supporting VK_EXT_metal_objects and rebuild the Vulkan device enabling the extension in the device extension list.
  3. Ensure the GraphicsDevice creation requests VK_EXT_metal_objects (and dependency VK_KHR_portability_subset where needed) rather than using a default device config.
  4. Verify you are actually on the Vulkan/MoltenVK backend and not another backend misconfigured to use the Vulkan IOSurface path.

Example fix

// before
var texture = Texture.NewFromIOSurface(device, surfaceHandle, width, height);

// after
if (device.HasMetalObjectsSupport)
    texture = Texture.NewFromIOSurface(device, surfaceHandle, width, height);
else
    texture = UploadSurfaceData(device, surfaceHandle, width, height);
Defensive patterns

Strategy: validation

Validate before calling

if (device == null || ioSurface == IntPtr.Zero) throw new ArgumentException("Invalid device or IOSurface.");
if (!device.HasMetalObjectsSupport)
    throw new NotSupportedException("VK_EXT_metal_objects unavailable; IOSurface import not supported.");

Type guard

bool CanImportIOSurface(GraphicsDevice device) => device != null && device.HasMetalObjectsSupport;

Try / catch

try
{
    texture = Texture.NewFromIOSurface(device, ioSurface, width, height);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("VK_EXT_metal_objects"))
{
    texture = UploadSurfaceData(device, ioSurface, width, height);
}

Prevention

When it happens

Trigger: Calling Texture.NewFromIOSurface(device, ioSurface, w, h) on a GraphicsDevice whose physical device lacks VK_EXT_metal_objects, or where the extension was not enabled at device creation; using a Vulkan ICD other than MoltenVK with no Metal interop support.

Common situations: macOS builds with a non-MoltenVK Vulkan loader/ICD; old MoltenVK versions before VK_EXT_metal_objects support; creating the Vulkan device without requesting VK_EXT_metal_objects; attempting IOSurface sharing on iOS where support configuration differs.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Graphics/Vulkan/Texture.Apple.Vulkan.cs:60

        {
            if (ioSurfaceRef != IntPtr.Zero)
            {
                CFRelease(ioSurfaceRef);
                ioSurfaceRef = IntPtr.Zero;
            }
        }

        /// <summary>
        /// Imports a CoreVideo IOSurface as a sampleable BGRA8_sRGB <see cref="Texture"/> via VK_EXT_metal_objects.
        /// Retains the IOSurface for the lifetime of the returned Texture; the caller is free to dispose its
        /// CVPixelBuffer once this call returns.
        /// </summary>
        public static unsafe Texture NewFromIOSurface(GraphicsDevice device, IntPtr ioSurface, int width, int height)
        {
            if (device == null) throw new ArgumentNullException(nameof(device));
            if (ioSurface == IntPtr.Zero) throw new ArgumentNullException(nameof(ioSurface));
            if (!device.HasMetalObjectsSupport)
                throw new InvalidOperationException("IOSurface import requires VK_EXT_metal_objects.");

            CFRetain(ioSurface);

            var ioSurfaceInfo = new VkImportMetalIOSurfaceInfoEXT
            {
                sType = VkStructureType.ImportMetalIOSurfaceInfoEXT,
                ioSurface = ioSurface,
            };

            var imageCreateInfo = new VkImageCreateInfo
            {
                sType = VkStructureType.ImageCreateInfo,
                pNext = &ioSurfaceInfo,
                imageType = VkImageType.Image2D,
                format = VkFormat.B8G8R8A8Srgb,
                extent = new VkExtent3D(width, height, 1),
                mipLevels = 1,
                arrayLayers = 1,

View on GitHub (pinned to 96fad776d2)