1

I have the keen idea to write a wrapper for Vulkan in c#. Unfortunately the second call of the Vulkan API already fails unexplainably. The code below is taken from Sascha Willems' Vulkan examples and converted into c# code:

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

with

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
    {
    }
}

My Suspicion is that Vk.Instance should be something else than just an empty class. VkInstance is in the official vulkan.h defined as typedef struct VkInstance_T* VkInstance. My understanding of this line is unfortunately very limited. I already tried exchanging the type Vk.Instance with IntPtr and object but without success.

Theses are important segments from 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);
CWe
  • 23
  • 4
  • You should probably use `IntPtr` for `theInstance`. As far as I understand it is the responsibility of the `vkCreateInstance` to create them. – Eugene Podskal Feb 22 '16 at 20:22
  • Anything that's a pointer in the C API should probably be an IntPtr in your PInvoke signatures and then use the Marshal.xxx() methods to read/write them. You may also be able to get away with unsafe blocks and struct pointers in them (note: you will usually want struct, not class, for C structs (because of vtables & the like)). In addition, VkInstance and VkPhysicalDevice are already pointers, so those last arguments are pointers to pointers; for CreateInstance "out IntPtr pInstance" would probably work; fir the enum you'll likely want to marshal count+ptr as an array. – Zastai Feb 22 '16 at 20:31
  • Generally speaking, it can be far more desirable to look at using something like swig to generate the basic interop layer, then possibly adding some OO model around that. – Zastai Feb 22 '16 at 20:32
  • Zastai, if you want the fame, I will mark your answer as accepted solution if you post one. – CWe Feb 22 '16 at 20:46
  • There are loads and loads of errors here. It looks like you are a pinvoke novice. That's fine, but you aren't going to succeed for a very long time at this rate. You'd be better building your skills with an easier library. Frankly though C++/CLI might be cleaner for you. – David Heffernan Feb 22 '16 at 20:53

1 Answers1

0

Zastai answered my question. It had to be

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

because it were indeed pointers to pointers.

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

Thanks a lot!

CWe
  • 23
  • 4