AccessViolationException 当通过 pInvoke 从 c# 调用 vkEnumeratePhys

本文关键字:调用 vkEnumeratePhys pInvoke AccessViolationException | 更新日期: 2023-09-27 18:35:41

我有一个敏锐的想法,用c#为Vulkan编写一个包装器。不幸的是,Vulkan API 的第二次调用已经莫名其妙地失败了。下面的代码取自 Sascha Willems 的 Vulkan 示例,并转换为 c# 代码:

Vk.ApplicationInfo applicationInfo = new Vk.ApplicationInfo();
applicationInfo.sType = Vk.StructureType.STRUCTURE_TYPE_APPLICATION_INFO;
applicationInfo.pApplicationName = "Example";
applicationInfo.pEngineName = "Example";
applicationInfo.apiVersion = (uint)Math.Pow(2, 22) + 2;
string[] enabledExtensions = new string[] { "VK_KHR_surface", "VK_KHR_win32_surface" };
Vk.InstanceCreateInfo instanceCreateInfo = new Vk.InstanceCreateInfo();
instanceCreateInfo.sType = Vk.StructureType.STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pNext = null;
instanceCreateInfo.pApplicationInfo = applicationInfo;
instanceCreateInfo.enabledExtensionCount = (uint)enabledExtensions.Count();
instanceCreateInfo.ppEnabledExtensionNames = enabledExtensions;

Vk.Instance theInstance = new Vk.Instance();
Vk.Result vr = Vk.vkCreateInstance(instanceCreateInfo, IntPtr.Zero, theInstance);
// vr = SUCCESS
uint gpuCount = 0;
vr = Vk.vkEnumeratePhysicalDevices(theInstance, ref gpuCount, IntPtr.Zero);
//Fails with System.AccessViolationException

public static class Vk
{
    public enum Result
    {
        SUCCESS = 0,
        ...
    };

    public enum StructureType
    {
        ...
    }
    static Vk()
    {
        List<string> path = new List<string>() { @"C:'VulkanSDK'1.0.3.1'Source'lib32'" };
        AddEnvironmentPaths(path);
    }
    static void AddEnvironmentPaths(IEnumerable<string> paths)
    {
        var path = new[] { Environment.GetEnvironmentVariable("PATH") ?? string.Empty };
        string newPath = string.Join(Path.PathSeparator.ToString(), path.Concat(paths));
        Environment.SetEnvironmentVariable("PATH", newPath);
    }

    [DllImport("vulkan-1.dll")]
    public static extern Result vkCreateInstance(InstanceCreateInfo instanceCreateInfo, IntPtr pAllocator, Instance instance);
    [StructLayout(LayoutKind.Sequential)]
    public class InstanceCreateInfo
    {
        public StructureType sType;
        public object pNext;
        public uint flags;
        public ApplicationInfo pApplicationInfo;
        public uint enabledLayerCount;
        public string[] ppEnabledLayerNames;
        public uint enabledExtensionCount;
        public string[] ppEnabledExtensionNames;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class ApplicationInfo
    {
        public StructureType sType;
        public object pNext;
        public string pApplicationName;
        public uint applicationVersion;
        public string pEngineName;
        public uint engineVersion;
        public uint apiVersion;
    }
    [StructLayout(LayoutKind.Sequential)]
    public class Instance
    {
    }
    [DllImport("vulkan-1.dll")]
    public static extern Result vkEnumeratePhysicalDevices(Instance instance, ref uint pPhysicalDeviceCount, PhysicalDevice pPhysicalDevices);
    [DllImport("vulkan-1.dll")]
    public static extern Result vkEnumeratePhysicalDevices(Instance instance, ref uint pPhysicalDeviceCount, IntPtr pPhysicalDevices);

    public class PhysicalDevice
    {
    }
}

我的怀疑是Vk.Instance应该不仅仅是一个空类。 VkInstance在官方的vulkan.h中定义为typedef struct VkInstance_T* VkInstance。不幸的是,我对这条线的理解非常有限。我已经尝试将类型 Vk.InstanceIntPtrobject 交换,但没有成功。

论文是vulkan.h的重要部分

#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
VK_DEFINE_HANDLE(VkInstance)
VK_DEFINE_HANDLE(VkPhysicalDevice)
VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(
    const VkInstanceCreateInfo* pCreateInfo,
    const VkAllocationCallbacks* pAllocator,
    VkInstance* pInstance);
VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(
    VkInstance instance,
    uint32_t* pPhysicalDeviceCount,
    VkPhysicalDevice* pPhysicalDevices);

AccessViolationException 当通过 pInvoke 从 c# 调用 vkEnumeratePhys

>扎斯泰回答了我的问题。它必须是

[DllImport("vulkan-1.dll")]
public static extern Result vkCreateInstance(InstanceCreateInfo  instanceCreateInfo, IntPtr pAllocator, out IntPtr instance);

因为它确实是指向指针的指针。

IntPtr instance;
Vk.Result vr = Vk.vkCreateInstance(instanceCreateInfo, IntPtr.Zero, out instance);
uint gpuCount = 0;
vr = Vk.vkEnumeratePhysicalDevices(instance, ref gpuCount, IntPtr.Zero);

多谢!