Vulkan bindings (#1)

* Initial Vulkan support
This commit is contained in:
ReinUsesLisp 2018-07-31 23:06:22 -03:00 committed by emmaus
parent 562882ad0d
commit 33da7b7761
60 changed files with 11439 additions and 30 deletions

View file

@ -11,10 +11,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generator.Rewrite", "src\Ge
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK", "src\OpenTK\OpenTK.csproj", "{A37A7E14-0000-0000-0000-000000000000}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK.Android", "src\OpenTK\OpenTK.Android.csproj", "{1648ACE1-E7D0-41CB-9A0E-C2A9D76C13A9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK.iOS", "src\OpenTK\OpenTK.iOS.csproj", "{88368190-E3DF-4EBE-ACAA-7B1779F376CA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK.GLControl", "src\OpenTK.GLControl\OpenTK.GLControl.csproj", "{A625BE88-0000-0000-0000-000000000000}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK.GLWidget", "src\OpenTK.GLWidget\OpenTK.GLWidget.csproj", "{A625BE87-0000-0000-0000-000000000000}"
@ -45,6 +41,10 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "OpenTK.Tests.Generators", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTK.Standard", "src\OpenTK\OpenTK.Standard.csproj", "{67F02FD3-8F7F-4D89-8551-359993271CA3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "vk.generator", "src\vk.generator\vk.generator.csproj", "{3766DF33-E28B-402D-9EE1-975C2AFB5EAF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "vk.rewrite", "src\vk.rewrite\vk.rewrite.csproj", "{3E9B1C4D-BCB4-418A-A94E-DD164A19C6A2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -67,14 +67,6 @@ Global
{A37A7E14-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A37A7E14-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A37A7E14-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU
{1648ACE1-E7D0-41CB-9A0E-C2A9D76C13A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1648ACE1-E7D0-41CB-9A0E-C2A9D76C13A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1648ACE1-E7D0-41CB-9A0E-C2A9D76C13A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1648ACE1-E7D0-41CB-9A0E-C2A9D76C13A9}.Release|Any CPU.Build.0 = Release|Any CPU
{88368190-E3DF-4EBE-ACAA-7B1779F376CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{88368190-E3DF-4EBE-ACAA-7B1779F376CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88368190-E3DF-4EBE-ACAA-7B1779F376CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88368190-E3DF-4EBE-ACAA-7B1779F376CA}.Release|Any CPU.Build.0 = Release|Any CPU
{A625BE88-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A625BE88-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A625BE88-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -103,6 +95,14 @@ Global
{67F02FD3-8F7F-4D89-8551-359993271CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67F02FD3-8F7F-4D89-8551-359993271CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67F02FD3-8F7F-4D89-8551-359993271CA3}.Release|Any CPU.Build.0 = Release|Any CPU
{3766DF33-E28B-402D-9EE1-975C2AFB5EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3766DF33-E28B-402D-9EE1-975C2AFB5EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3766DF33-E28B-402D-9EE1-975C2AFB5EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3766DF33-E28B-402D-9EE1-975C2AFB5EAF}.Release|Any CPU.Build.0 = Release|Any CPU
{3E9B1C4D-BCB4-418A-A94E-DD164A19C6A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E9B1C4D-BCB4-418A-A94E-DD164A19C6A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E9B1C4D-BCB4-418A-A94E-DD164A19C6A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E9B1C4D-BCB4-418A-A94E-DD164A19C6A2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -1,6 +1,8 @@
OpenTK
======
This fork contains modified dependencies from mellinoe's Vulkan bindings, you can get the original from [here](https://github.com/mellinoe/vk).
### MAINTAINERS WANTED

View file

@ -164,9 +164,9 @@ namespace OpenTK
return t != null;
}
#if SDL2
private static bool DetectSdl2()
{
#if SDL2
bool supported = false;
// Detect whether SDL2 is supported
@ -220,8 +220,10 @@ namespace OpenTK
}
return supported;
}
#endif
#else
return false;
#endif
}
private static void DetectUnix(out bool unix, out bool linux, out bool macos)
{
@ -298,7 +300,7 @@ namespace OpenTK
initialized = true;
#endif
Debug.Print("Detected configuration: {0} / {1}",
Debug.Print("Detected configuration: {0} / {1}",
RunningOnWindows ? "Windows" : RunningOnLinux ? "Linux" : RunningOnMacOS ? "MacOS" :
runningOnUnix ? "Unix" : RunningOnX11 ? "X11" : "Unknown Platform",
RunningOnMono ? "Mono" : ".Net");

1
src/OpenTK/Graphics/Vulkan/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
Generated/

View file

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace OpenTK.Graphics.Vulkan
{
public static class BindingsHelpers
{
public static unsafe StringHandle StringToHGlobalUtf8(string s)
{
int byteCount = Encoding.UTF8.GetByteCount(s);
IntPtr retPtr = Marshal.AllocHGlobal(byteCount);
fixed (char* stringPtr = s)
{
Encoding.UTF8.GetBytes(stringPtr, s.Length, (byte*)retPtr.ToPointer(), byteCount);
}
return new StringHandle() { Handle = retPtr };
}
public static void FreeHGlobal(StringHandle ptr)
{
Marshal.FreeHGlobal(ptr.Handle);
}
}
public struct StringHandle
{
public IntPtr Handle;
}
}

View file

@ -0,0 +1,8 @@
using System;
namespace OpenTK.Graphics.Vulkan.Generator
{
internal class CalliRewriteAttribute : Attribute
{
}
}

View file

@ -0,0 +1,179 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace OpenTK.Graphics.Vulkan
{
public static partial class VK
{
private static NativeLibrary s_nativeLib;
static public void LoadFunctions()
{
if (s_nativeLib == null)
{
s_nativeLib = LoadNativeLibrary();
}
LoadFunctionPointers();
}
public static IntPtr LoadFunctionPointer(string functionName)
{
if (s_nativeLib == null)
{
s_nativeLib = LoadNativeLibrary();
}
return s_nativeLib.LoadFunctionPointer(functionName);
}
private static NativeLibrary LoadNativeLibrary()
{
return NativeLibrary.Load(GetVulkanName());
}
private static string GetVulkanName()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "vulkan-1.dll";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
if (RuntimeInformation.OSDescription.Contains("Unix"))
{
// Android
return "libvulkan.so";
}
else
{
// Desktop Linux
return "libvulkan.so.1";
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "libvulkan.dylib";
}
else
{
throw new PlatformNotSupportedException();
}
}
private static Exception CreateMissingFunctionException()
{
return new InvalidOperationException("The function does not exist or could not be loaded.");
}
}
public abstract class NativeLibrary : IDisposable
{
private readonly string _libraryName;
private readonly IntPtr _libraryHandle;
public IntPtr NativeHandle => _libraryHandle;
public NativeLibrary(string libraryName)
{
_libraryName = libraryName;
_libraryHandle = LoadLibrary(_libraryName);
if (_libraryHandle == IntPtr.Zero)
{
throw new InvalidOperationException("Could not load " + libraryName);
}
}
protected abstract IntPtr LoadLibrary(string libraryName);
protected abstract void FreeLibrary(IntPtr libraryHandle);
protected abstract IntPtr LoadFunction(string functionName);
public IntPtr LoadFunctionPointer(string functionName)
{
if (functionName == null)
{
throw new ArgumentNullException(nameof(functionName));
}
return LoadFunction(functionName);
}
public void Dispose()
{
FreeLibrary(_libraryHandle);
}
public static NativeLibrary Load(string libraryName)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return new WindowsNativeLibrary(libraryName);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
|| RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return new UnixNativeLibrary(libraryName);
}
else
{
throw new PlatformNotSupportedException("Cannot load native libraries on this platform: " + RuntimeInformation.OSDescription);
}
}
private class WindowsNativeLibrary : NativeLibrary
{
public WindowsNativeLibrary(string libraryName) : base(libraryName)
{
}
protected override IntPtr LoadLibrary(string libraryName)
{
return Kernel32.LoadLibrary(libraryName);
}
protected override void FreeLibrary(IntPtr libraryHandle)
{
Kernel32.FreeLibrary(libraryHandle);
}
protected override IntPtr LoadFunction(string functionName)
{
Debug.WriteLine("Loading " + functionName);
return Kernel32.GetProcAddress(NativeHandle, functionName);
}
}
private class UnixNativeLibrary : NativeLibrary
{
public UnixNativeLibrary(string libraryName) : base(libraryName)
{
}
protected override IntPtr LoadLibrary(string libraryName)
{
Libdl.dlerror();
IntPtr handle = Libdl.dlopen(libraryName, Libdl.RTLD_NOW);
if (handle == IntPtr.Zero && !Path.IsPathRooted(libraryName))
{
string localPath = Path.Combine(AppContext.BaseDirectory, libraryName);
handle = Libdl.dlopen(localPath, Libdl.RTLD_NOW);
}
return handle;
}
protected override void FreeLibrary(IntPtr libraryHandle)
{
Libdl.dlclose(libraryHandle);
}
protected override IntPtr LoadFunction(string functionName)
{
return Libdl.dlsym(NativeHandle, functionName);
}
}
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace OpenTK.Graphics.Vulkan
{
public static partial class VK
{
public static readonly IntPtr NullHandle = IntPtr.Zero;
}
}

View file

@ -0,0 +1,45 @@
using System;
namespace OpenTK.Graphics.Vulkan
{
public unsafe delegate void* PFN_vkAllocationFunction(
void* pUserData,
UIntPtr size,
UIntPtr alignment,
VkSystemAllocationScope allocationScope);
public unsafe delegate void* PFN_vkReallocationFunction(
void* pUserData,
void* pOriginal,
UIntPtr size,
UIntPtr alignment,
VkSystemAllocationScope allocationScope);
public unsafe delegate void PFN_vkFreeFunction(
void* pUserData,
void* pMemory);
public unsafe delegate void PFN_vkInternalAllocationNotification(
void* pUserData,
UIntPtr size,
VkInternalAllocationType allocationType,
VkSystemAllocationScope allocationScope);
public unsafe delegate void PFN_vkInternalFreeNotification(
void* pUserData,
UIntPtr size,
VkInternalAllocationType allocationType,
VkSystemAllocationScope allocationScope);
public unsafe delegate void PFN_vkVoidFunction();
public unsafe delegate uint PFN_vkDebugReportCallbackEXT(
uint flags,
VkDebugReportObjectTypeEXT objectType,
ulong @object,
UIntPtr location,
int messageCode,
byte* pLayerPrefix,
byte* pMessage,
void* pUserData);
}

View file

@ -0,0 +1,83 @@
using System;
namespace OpenTK.Graphics.Vulkan
{
// Windows
namespace Win32
{
public struct HINSTANCE
{
public IntPtr Handle;
public static implicit operator IntPtr(HINSTANCE hinst) => hinst.Handle;
public static implicit operator HINSTANCE(IntPtr handle) => new HINSTANCE() { Handle = handle };
}
public struct HWND
{
public IntPtr Handle;
public static implicit operator IntPtr(HWND hwnd) => hwnd.Handle;
public static implicit operator HWND(IntPtr handle) => new HWND() { Handle = handle };
}
public struct HANDLE
{
public IntPtr Handle;
public static implicit operator IntPtr(HANDLE handle) => handle.Handle;
public static implicit operator HANDLE(IntPtr handle) => new HANDLE() { Handle = handle };
}
public struct SECURITY_ATTRIBUTES
{
public uint nLength;
public IntPtr lpSecurityDescriptor;
public uint bInheritHandle;
}
}
// Android
namespace Android
{
public struct ANativeWindow { }
}
// Linux
namespace Mir
{
public struct MirConnection { }
public struct MirSurface { }
}
namespace Wayland
{
public struct wl_display { }
public struct wl_surface { }
}
namespace Xlib
{
public struct Display { }
public struct Window
{
public IntPtr Value;
}
public struct VisualID
{
public ulong ID;
public static implicit operator VisualID(ulong value) => new VisualID() { ID = value };
public static implicit operator ulong(VisualID id) => id.ID;
}
}
namespace Xcb
{
public struct xcb_connection_t { }
public struct xcb_window_t { }
public struct xcb_visualid_t
{
public uint ID;
public static implicit operator xcb_visualid_t(uint value) => new xcb_visualid_t() { ID = value };
public static implicit operator uint(xcb_visualid_t id) => id.ID;
}
}
}

View file

@ -0,0 +1,15 @@
using System;
using System.Runtime.InteropServices;
namespace OpenTK.Graphics.Vulkan
{
public struct FunctionPointer<TFunc>
{
public IntPtr Pointer;
public FunctionPointer(TFunc func)
{
Pointer = Marshal.GetFunctionPointerForDelegate(func);
}
}
}

View file

@ -0,0 +1,17 @@
using System;
using System.Runtime.InteropServices;
namespace OpenTK.Graphics.Vulkan
{
internal static class Kernel32
{
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string fileName);
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr module, string procName);
[DllImport("kernel32")]
public static extern int FreeLibrary(IntPtr module);
}
}

View file

@ -0,0 +1,22 @@
using System;
using System.Runtime.InteropServices;
namespace OpenTK.Graphics.Vulkan
{
internal static class Libdl
{
[DllImport("libdl")]
public static extern IntPtr dlopen(string fileName, int flags);
[DllImport("libdl")]
public static extern IntPtr dlsym(IntPtr handle, string name);
[DllImport("libdl")]
public static extern int dlclose(IntPtr handle);
[DllImport("libdl")]
public static extern string dlerror();
public const int RTLD_NOW = 0x002;
}
}

View file

@ -0,0 +1,68 @@
using System;
namespace OpenTK.Graphics.Vulkan
{
/// <summary>
/// A boolean value stored in a 4-byte unsigned integer.
/// </summary>
public struct VkBool32 : IEquatable<VkBool32>
{
/// <summary>
/// The raw value of the <see cref="VkBool32"/>. A value of 0 represents "false", all other values represent "true".
/// </summary>
public uint Value;
/// <summary>
/// Constructs a new <see cref="VkBool32"/> with the given raw value.
/// </summary>
/// <param name="value"></param>
public VkBool32(uint value)
{
Value = value;
}
/// <summary>
/// Represents the boolean "true" value. Has a raw value of 1.
/// </summary>
public static readonly VkBool32 True = new VkBool32(1);
/// <summary>
/// Represents the boolean "true" value. Has a raw value of 0.
/// </summary>
public static readonly VkBool32 False = new VkBool32(0);
/// <summary>
/// Returns whether another <see cref="VkBool32"/> value is considered equal to this one.
/// Two <see cref="VkBool32"/>s are considered equal when their raw values are equal.
/// </summary>
/// <param name="other">The value to compare to.</param>
/// <returns>True if the other value's underlying raw value is equal to this instance's. False otherwise.</returns>
public bool Equals(VkBool32 other)
{
return Value.Equals(other.Value);
}
public override bool Equals(object obj)
{
return obj is VkBool32 b && Equals(b);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public override string ToString()
{
return $"{(this ? "True" : "False")} ({Value})";
}
public static implicit operator bool(VkBool32 b) => b.Value != 0;
public static implicit operator uint(VkBool32 b) => b.Value;
public static implicit operator VkBool32(bool b) => b ? True : False;
public static implicit operator VkBool32(uint value) => new VkBool32(value);
public static bool operator ==(VkBool32 left, VkBool32 right) => left.Value == right.Value;
public static bool operator !=(VkBool32 left, VkBool32 right) => left.Value != right.Value;
}
}

View file

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace OpenTK.Graphics.Vulkan
{
public partial struct VkClearColorValue
{
public VkClearColorValue(float r, float g, float b, float a = 1.0f) : this()
{
float32_0 = r;
float32_1 = g;
float32_2 = b;
float32_3 = a;
}
public VkClearColorValue(int r, int g, int b, int a = 255) : this()
{
int32_0 = r;
int32_1 = g;
int32_2 = b;
int32_3 = a;
}
public VkClearColorValue(uint r, uint g, uint b, uint a = 255) : this()
{
uint32_0 = r;
uint32_1 = g;
uint32_2 = b;
uint32_3 = a;
}
}
}

View file

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace OpenTK.Graphics.Vulkan
{
public unsafe partial struct VkClearDepthStencilValue
{
public VkClearDepthStencilValue(float depth, uint stencil)
{
this.depth = depth;
this.stencil = stencil;
}
}
}

View file

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Graphics.Vulkan
{
/// <summary>
/// Structure specifying a two-dimensional extent.
/// </summary>
public unsafe partial struct VkExtent2D : IEquatable<VkExtent2D>
{
/// <summary>
/// An <see cref="VkExtent2D"/> with all of its components set to zero.
/// </summary>
public static readonly VkExtent2D Zero = new VkExtent2D(0, 0);
/// <summary>
/// Initializes a new instance of <see cref="VkExtent2D"/> structure.
/// </summary>
/// <param name="width">The width component of the extent.</param>
/// <param name="height">The height component of the extent.</param>
public VkExtent2D(uint width, uint height)
{
this.width = width;
this.height = height;
}
/// <summary>
/// Initializes a new instance of <see cref="VkExtent2D"/> structure.
/// </summary>
/// <param name="width">The width component of the extent.</param>
/// <param name="height">The height component of the extent.</param>
public VkExtent2D(int width, int height)
{
this.width = (uint)width;
this.height = (uint)height;
}
/// <summary>
/// Determines whether the specified <see cref="VkExtent2D"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="VkExtent2D"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="VkExtent2D"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public bool Equals(ref VkExtent2D other)
{
return other.width == width && other.height == height;
}
/// <summary>
/// Determines whether the specified <see cref="VkExtent2D"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="VkExtent2D"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="VkExtent2D"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public bool Equals(VkExtent2D other)
{
return Equals(ref other);
}
/// <summary>
/// Determines whether the specified <see cref="object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
return obj is VkExtent2D && Equals((VkExtent2D)obj);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
unchecked
{
int hashCode = width.GetHashCode();
hashCode = (hashCode * 397) ^ height.GetHashCode();
return hashCode;
}
}
/// <summary>
/// Returns a boolean indicating whether the two given extents are equal.
/// </summary>
/// <param name="left">The first extent to compare.</param>
/// <param name="right">The second extent to compare.</param>
/// <returns><c>true</c> if the extents are equal; <c>false</c> otherwise.</returns>
public static bool operator ==(VkExtent2D left, VkExtent2D right) => left.Equals(ref right);
/// <summary>
/// Returns a boolean indicating whether the two given extents are not equal.
/// </summary>
/// <param name="left">The first extent to compare.</param>
/// <param name="right">The second extent to compare.</param>
/// <returns>
/// <c>true</c> if the extents are not equal; <c>false</c> if they are equal.
/// </returns>
public static bool operator !=(VkExtent2D left, VkExtent2D right) => !left.Equals(ref right);
}
}

View file

@ -0,0 +1,27 @@
namespace OpenTK.Graphics.Vulkan
{
public unsafe partial struct VkImageMemoryBarrier
{
public VkImageMemoryBarrier(
VkImage image,
VkImageSubresourceRange subresourceRange,
VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask,
VkImageLayout oldLayout,
VkImageLayout newLayout,
uint srcQueueFamilyIndex = VK.QueueFamilyIgnored,
uint dstQueueFamilyIndex = VK.QueueFamilyIgnored)
{
sType = VkStructureType.ImageMemoryBarrier;
pNext = null;
this.srcAccessMask = srcAccessMask;
this.dstAccessMask = dstAccessMask;
this.oldLayout = oldLayout;
this.newLayout = newLayout;
this.srcQueueFamilyIndex = srcQueueFamilyIndex;
this.dstQueueFamilyIndex = dstQueueFamilyIndex;
this.image = image;
this.subresourceRange = subresourceRange;
}
}
}

View file

@ -0,0 +1,17 @@
namespace OpenTK.Graphics.Vulkan
{
public unsafe partial struct VkImageSubresourceRange
{
public VkImageSubresourceRange(
VkImageAspectFlags aspectMask,
uint baseMipLevel = 0, uint levelCount = 1,
uint baseArrayLayer = 0, uint layerCount = 1)
{
this.aspectMask = aspectMask;
this.baseMipLevel = baseMipLevel;
this.levelCount = levelCount;
this.baseArrayLayer = baseArrayLayer;
this.layerCount = layerCount;
}
}
}

View file

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Graphics.Vulkan
{
/// <summary>
/// Structure specifying a two-dimensional offset.
/// </summary>
public unsafe partial struct VkOffset2D : IEquatable<VkOffset2D>
{
/// <summary>
/// An <see cref="VkOffset2D"/> with all of its components set to zero.
/// </summary>
public static readonly VkOffset2D Zero;
public VkOffset2D(int x, int y)
{
this.x = x;
this.y = y;
}
/// <summary>
/// Determines whether the specified <see cref="VkOffset2D"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="VkOffset2D"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="VkOffset2D"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public bool Equals(ref VkOffset2D other)
{
return other.x == x && other.y == y;
}
/// <summary>
/// Determines whether the specified <see cref="VkOffset2D"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="VkOffset2D"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="VkOffset2D"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public bool Equals(VkOffset2D other)
{
return Equals(ref other);
}
/// <summary>
/// Determines whether the specified <see cref="object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
return obj is VkOffset2D && Equals((VkOffset2D)obj);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
unchecked
{
int hashCode = x;
hashCode = (hashCode * 397) ^ y;
return hashCode;
}
}
/// <summary>
/// Returns a boolean indicating whether the two given offsets are equal.
/// </summary>
/// <param name="left">The first offset to compare.</param>
/// <param name="right">The second offset to compare.</param>
/// <returns><c>true</c> if the offsets are equal; <c>false</c> otherwise.</returns>
public static bool operator ==(VkOffset2D left, VkOffset2D right) => left.Equals(ref right);
/// <summary>
/// Returns a boolean indicating whether the two given offsets are not equal.
/// </summary>
/// <param name="left">The first offset to compare.</param>
/// <param name="right">The second offset to compare.</param>
/// <returns>
/// <c>true</c> if the offsets are not equal; <c>false</c> if they are equal.
/// </returns>
public static bool operator !=(VkOffset2D left, VkOffset2D right) => !left.Equals(ref right);
}
}

View file

@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace OpenTK.Graphics.Vulkan
{
/// <summary>
/// Structure specifying a two-dimensional subregion.
/// </summary>
public unsafe partial struct VkRect2D : IEquatable<VkRect2D>
{
/// <summary>
/// An <see cref="VkRect2D"/> with all of its components set to zero.
/// </summary>
public static readonly VkRect2D Zero = new VkRect2D(VkOffset2D.Zero, VkExtent2D.Zero);
/// <summary>
/// Initializes a new instance of the <see cref="VkRect2D"/> structure.
/// </summary>
/// <param name="offset">The offset component of the rectangle.</param>
/// <param name="extent">The extent component of the rectangle.</param>
public VkRect2D(VkOffset2D offset, VkExtent2D extent)
{
this.offset = offset;
this.extent = extent;
}
/// <summary>
/// Initializes a new instance of the <see cref="VkRect2D"/> structure.
/// </summary>
/// <param name="extent">The extent component of the rectangle.</param>
public VkRect2D(VkExtent2D extent)
{
this.offset = default(VkOffset2D);
this.extent = extent;
}
/// <summary>
/// Initializes a new instance of the <see cref="Rect2D"/> structure.
/// </summary>
/// <param name="x">The X component of the offset.</param>
/// <param name="y">The Y component of the offset.</param>
/// <param name="width">The width component of the extent.</param>
/// <param name="height">The height component of the extent.</param>
public VkRect2D(int x, int y, uint width, uint height)
{
this.offset = new VkOffset2D(x, y);
this.extent = new VkExtent2D(width, height);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rect2D"/> structure.
/// </summary>
/// <param name="x">The X component of the offset.</param>
/// <param name="y">The Y component of the offset.</param>
/// <param name="width">The width component of the extent.</param>
/// <param name="height">The height component of the extent.</param>
public VkRect2D(int x, int y, int width, int height)
{
this.offset = new VkOffset2D(x, y);
this.extent = new VkExtent2D(width, height);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rect2D"/> structure.
/// </summary>
/// <param name="width">The width component of the extent.</param>
/// <param name="height">The height component of the extent.</param>
public VkRect2D(uint width, uint height)
{
this.offset = default(VkOffset2D);
this.extent = new VkExtent2D(width, height);
}
/// <summary>
/// Determines whether the specified <see cref="VkRect2D"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="VkRect2D"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="VkOffset2D"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public bool Equals(ref VkRect2D other)
{
return other.offset.Equals(ref offset) && other.extent.Equals(ref extent);
}
/// <summary>
/// Determines whether the specified <see cref="VkRect2D"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="VkRect2D"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="VkRect2D"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public bool Equals(VkRect2D other)
{
return Equals(ref other);
}
/// <summary>
/// Determines whether the specified <see cref="object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
return obj is VkRect2D && Equals((VkRect2D)obj);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
unchecked
{
int hashCode = extent.GetHashCode();
hashCode = (hashCode * 397) ^ offset.GetHashCode();
return hashCode;
}
}
/// <summary>
/// Returns a boolean indicating whether the two given rectangles are equal.
/// </summary>
/// <param name="left">The first rectangle to compare.</param>
/// <param name="right">The second rectangle to compare.</param>
/// <returns><c>true</c> if the rectangles are equal; <c>false</c> otherwise.</returns>
public static bool operator ==(VkRect2D left, VkRect2D right) => left.Equals(right);
/// <summary>
/// Returns a boolean indicating whether the two given rectangles are not equal.
/// </summary>
/// <param name="left">The first rectangle to compare.</param>
/// <param name="right">The second rectangle to compare.</param>
/// <returns>
/// <c>true</c> if the rectangles are not equal; <c>false</c> if they are equal.
/// </returns>
public static bool operator !=(VkRect2D left, VkRect2D right) => !left.Equals(right);
}
}

View file

@ -0,0 +1,552 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if !VALIDATE
using System.Diagnostics;
#endif
namespace OpenTK.Graphics.Vulkan
{
using Debug = System.Diagnostics.Debug;
public unsafe class VulkanList<T> : IEnumerable<T>, IDisposable where T : struct
{
private byte* _dataPtr;
private uint _elementCapacity;
private uint _count;
public const int DefaultCapacity = 4;
private const float GrowthFactor = 2f;
private static readonly uint s_elementByteSize = InitializeTypeSize();
public VulkanList() : this(DefaultCapacity) { }
public VulkanList(uint capacity)
{
Allocate(capacity);
}
public VulkanList(uint capacity, uint count)
{
Allocate(capacity);
Count = count;
}
public VulkanList(VulkanList<T> existingList)
{
Allocate(existingList._elementCapacity);
Unsafe.CopyBlock(_dataPtr, existingList._dataPtr, existingList._count * s_elementByteSize);
}
public static VulkanList<T> New(uint count)
{
return new VulkanList<T>(count, count);
}
public IntPtr Data
{
get
{
ThrowIfDisposed();
return new IntPtr(_dataPtr);
}
}
public uint Count
{
get
{
ThrowIfDisposed();
return _count;
}
set
{
ThrowIfDisposed();
if (value > _elementCapacity)
{
uint newLements = value - Count;
CoreResize(value);
Unsafe.InitBlock(_dataPtr + _count * s_elementByteSize, 0, newLements * s_elementByteSize);
}
_count = value;
}
}
public ref T this[uint index]
{
get
{
ThrowIfDisposed();
#if VALIDATE
if (index >= _count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
#else
Debug.Assert(index < _count);
#endif
return ref Unsafe.AsRef<T>(_dataPtr + index * s_elementByteSize);
}
}
public ref T this[int index]
{
get
{
ThrowIfDisposed();
#if VALIDATE
if (index < 0 || index >= _count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
#else
Debug.Assert(index >= 0 && index < _count);
#endif
return ref Unsafe.AsRef<T>(_dataPtr + index * s_elementByteSize);
}
}
public ReadOnlyNativeListView<T> GetReadOnlyView()
{
ThrowIfDisposed();
return new ReadOnlyNativeListView<T>(this, 0, _count);
}
public ReadOnlyNativeListView<T> GetReadOnlyView(uint start, uint count)
{
ThrowIfDisposed();
#if VALIDATE
if (start + count > _count)
{
throw new ArgumentOutOfRangeException();
}
#else
Debug.Assert(start + count <= _count);
#endif
return new ReadOnlyNativeListView<T>(this, start, count);
}
public View<ViewType> GetView<ViewType>() where ViewType : struct
{
ThrowIfDisposed();
return new View<ViewType>(this);
}
public bool IsDisposed => _dataPtr == null;
public void Add(ref T item)
{
ThrowIfDisposed();
if (_count == _elementCapacity)
{
CoreResize((uint)(_elementCapacity * GrowthFactor));
}
Unsafe.Copy(_dataPtr + _count * s_elementByteSize, ref item);
_count += 1;
}
public void Add(T item)
{
ThrowIfDisposed();
if (_count == _elementCapacity)
{
CoreResize((uint)(_elementCapacity * GrowthFactor));
}
Unsafe.Write(_dataPtr + _count * s_elementByteSize, item);
_count += 1;
}
public void Add(void* data, uint numElements)
{
ThrowIfDisposed();
uint needed = _count + numElements;
if (numElements > _elementCapacity)
{
CoreResize((uint)(needed * GrowthFactor));
}
Unsafe.CopyBlock(_dataPtr + _count * s_elementByteSize, data, numElements * s_elementByteSize);
_count += numElements;
}
public bool Remove(ref T item)
{
ThrowIfDisposed();
bool result = IndexOf(ref item, out uint index);
if (result)
{
CoreRemoveAt(index);
}
return result;
}
public bool Remove(T item) => Remove(ref item);
public void RemoveAt(uint index)
{
ThrowIfDisposed();
#if VALIDATE
if (index >= _count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
#else
Debug.Assert(index < _count);
#endif
CoreRemoveAt(index);
}
public void Clear()
{
ThrowIfDisposed();
_count = 0;
}
public bool IndexOf(ref T item, out uint index)
{
ThrowIfDisposed();
byte* itemPtr = (byte*)Unsafe.AsPointer(ref item);
for (index = 0; index < _count; index++)
{
byte* ptr = _dataPtr + index * s_elementByteSize;
if (Equals(ptr, itemPtr, s_elementByteSize))
{
return true;
}
}
return false;
}
public bool IndexOf(T item, out uint index)
{
ThrowIfDisposed();
byte* itemPtr = (byte*)Unsafe.AsPointer(ref item);
for (index = 0; index < _count; index++)
{
byte* ptr = _dataPtr + index * s_elementByteSize;
if (Equals(ptr, itemPtr, s_elementByteSize))
{
return true;
}
}
return false;
}
public IntPtr GetAddress(uint index)
{
ThrowIfDisposed();
#if VALIDATE
if (index >= _count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
#else
Debug.Assert(index < _count);
#endif
return new IntPtr(_dataPtr + (index * s_elementByteSize));
}
public void Resize(uint elementCount)
{
ThrowIfDisposed();
CoreResize(elementCount);
if (_elementCapacity < _count)
{
_count = _elementCapacity;
}
}
private static uint InitializeTypeSize()
{
#if VALIDATE
// TODO: DHetermine if the structure type contains references and throw if it does.
// https://github.com/dotnet/corefx/issues/14047
#endif
return (uint)Unsafe.SizeOf<T>();
}
private void CoreResize(uint elementCount)
{
_dataPtr = (byte*)Marshal.ReAllocHGlobal(new IntPtr(_dataPtr), (IntPtr)(elementCount * s_elementByteSize));
_elementCapacity = elementCount;
}
private void Allocate(uint elementCount)
{
_dataPtr = (byte*)Marshal.AllocHGlobal((int)(elementCount * s_elementByteSize));
_elementCapacity = elementCount;
}
private bool Equals(byte* ptr, byte* itemPtr, uint s_elementByteSize)
{
for (int i = 0; i < s_elementByteSize; i++)
{
if (ptr[i] != itemPtr[i])
{
return false;
}
}
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void CoreRemoveAt(uint index)
{
Unsafe.CopyBlock(_dataPtr + index * s_elementByteSize, _dataPtr + (_count - 1) * s_elementByteSize, s_elementByteSize);
_count -= 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if !VALIDATE
[Conditional("DEBUG")]
#endif
private void ThrowIfDisposed()
{
#if VALIDATE
if (_dataPtr == null)
{
throw new ObjectDisposedException(nameof(Data));
}
#else
Debug.Assert(_dataPtr != null, "NativeList is disposed.");
#endif
}
public void Dispose()
{
ThrowIfDisposed();
Marshal.FreeHGlobal(new IntPtr(_dataPtr));
_dataPtr = null;
}
#if DEBUG
~NativeList()
{
if (_dataPtr != null)
{
Debug.WriteLine($"A NativeList<{typeof(T).Name}> was not properly disposed.");
Dispose();
}
}
#endif
public Enumerator GetEnumerator()
{
ThrowIfDisposed();
return new Enumerator(_dataPtr, _count);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<T>
{
private byte* _basePtr;
private uint _count;
private uint _currentIndex;
private T _current;
public Enumerator(byte* basePtr, uint count)
{
_basePtr = basePtr;
_count = count;
_currentIndex = 0;
_current = default(T);
}
public T Current => _current;
object IEnumerator.Current => Current;
public bool MoveNext()
{
if (_currentIndex != _count)
{
_current = Unsafe.Read<T>(_basePtr + _currentIndex * s_elementByteSize);
_currentIndex += 1;
return true;
}
return false;
}
public void Reset()
{
_current = default(T);
_currentIndex = 0;
}
public void Dispose() { }
}
public struct View<ViewType> : IEnumerable<ViewType> where ViewType : struct
{
private static readonly uint s_elementByteSize = (uint)Unsafe.SizeOf<ViewType>();
private readonly VulkanList<T> _parent;
public View(VulkanList<T> parent)
{
_parent = parent;
}
public uint Count => (_parent.Count * VulkanList<T>.s_elementByteSize) / s_elementByteSize;
public ViewType this[uint index]
{
get
{
#if VALIDATE
if (index >= Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
#else
Debug.Assert(index < Count);
#endif
return Unsafe.Read<ViewType>(_parent._dataPtr + index * s_elementByteSize);
}
}
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<ViewType> IEnumerable<ViewType>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<ViewType>
{
private View<ViewType> _view;
private uint _currentIndex;
private ViewType _current;
public Enumerator(View<ViewType> view)
{
_view = view;
_currentIndex = 0;
_current = default(ViewType);
}
public ViewType Current => _view[_currentIndex];
object IEnumerator.Current => Current;
public bool MoveNext()
{
if (_currentIndex != _view.Count)
{
_current = _view[_currentIndex];
_currentIndex += 1;
return true;
}
return false;
}
public void Reset()
{
_currentIndex = 0;
_current = default(ViewType);
}
public void Dispose() { }
}
}
}
public struct ReadOnlyNativeListView<T> : IEnumerable<T> where T : struct
{
private readonly VulkanList<T> _list;
private readonly uint _start;
public readonly uint Count;
public ReadOnlyNativeListView(VulkanList<T> list, uint start, uint count)
{
_list = list;
_start = start;
Count = count;
}
public T this[uint index]
{
get
{
#if VALIDATE
if (index >= Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
#else
Debug.Assert(index < Count);
#endif
return _list[index + _start];
}
}
public T this[int index]
{
get
{
#if VALIDATE
if (index < 0 || index >= Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
#else
Debug.Assert(index >= 0 && index < Count);
#endif
return _list[(uint)index + _start];
}
}
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<T>
{
private ReadOnlyNativeListView<T> _view;
private uint _currentIndex;
private T _current;
public Enumerator(ReadOnlyNativeListView<T> view)
{
_view = view;
_currentIndex = view._start;
_current = default(T);
}
public T Current => _view[_currentIndex];
object IEnumerator.Current => Current;
public bool MoveNext()
{
if (_currentIndex != _view._start + _view.Count)
{
_current = _view[_currentIndex];
_currentIndex += 1;
return true;
}
return false;
}
public void Reset()
{
_currentIndex = _view._start;
_current = default(T);
}
public void Dispose() { }
}
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace OpenTK.Graphics.Vulkan
{
public unsafe class VulkanString : IDisposable
{
private GCHandle _handle;
private uint _numBytes;
public byte* StringPtr => (byte*)_handle.AddrOfPinnedObject().ToPointer();
public VulkanString(string s)
{
if (s == null)
{
throw new ArgumentNullException(nameof(s));
}
byte[] text = Encoding.UTF8.GetBytes(s);
_handle = GCHandle.Alloc(text, GCHandleType.Pinned);
_numBytes = (uint)text.Length;
}
public void SetText(string s)
{
if (s == null)
{
throw new ArgumentNullException(nameof(s));
}
_handle.Free();
byte[] text = Encoding.UTF8.GetBytes(s);
_handle = GCHandle.Alloc(text, GCHandleType.Pinned);
_numBytes = (uint)text.Length;
}
private string GetString()
{
return Encoding.UTF8.GetString(StringPtr, (int)_numBytes);
}
public void Dispose()
{
_handle.Free();
}
public static implicit operator byte* (VulkanString utf8String) => utf8String.StringPtr;
public static implicit operator IntPtr (VulkanString utf8String) => new IntPtr(utf8String.StringPtr);
public static implicit operator VulkanString(string s) => new VulkanString(s);
public static implicit operator string(VulkanString utf8String) => utf8String.GetString();
}
}

View file

@ -0,0 +1,14 @@
namespace OpenTK.Graphics.Vulkan
{
public static class VulkanStrings
{
public static VulkanString VK_KHR_SURFACE_EXTENSION_NAME = "VK_KHR_surface";
public static VulkanString VK_KHR_WIN32_SURFACE_EXTENSION_NAME = "VK_KHR_win32_surface";
public static VulkanString VK_KHR_XCB_SURFACE_EXTENSION_NAME = "VK_KHR_xcb_surface";
public static VulkanString VK_KHR_XLIB_SURFACE_EXTENSION_NAME = "VK_KHR_xlib_surface";
public static VulkanString VK_KHR_SWAPCHAIN_EXTENSION_NAME = "VK_KHR_swapchain";
public static VulkanString VK_EXT_DEBUG_REPORT_EXTENSION_NAME = "VK_EXT_debug_report";
public static VulkanString StandardValidationLayerName = "VK_LAYER_LUNARG_standard_validation";
public static VulkanString main = "main";
}
}

View file

@ -0,0 +1,23 @@
namespace OpenTK.Graphics.Vulkan
{
public struct VulkanVersion
{
private readonly uint value;
public VulkanVersion(uint major, uint minor, uint patch)
{
value = major << 22 | minor << 12 | patch;
}
public uint Major => value >> 22;
public uint Minor => (value >> 12) & 0x3ff;
public uint Patch => (value >> 22) & 0xfff;
public static implicit operator uint(VulkanVersion version)
{
return version.value;
}
}
}

View file

@ -29,6 +29,7 @@ using System.ComponentModel;
using System.Drawing;
#endif
using OpenTK.Graphics;
using OpenTK.Graphics.Vulkan;
using OpenTK.Input;
using OpenTK.Platform;
@ -49,6 +50,55 @@ namespace OpenTK
private bool events;
private bool previous_cursor_visible = true;
public unsafe VkResult CreateVulkanSurface(VkInstance instance, out VkSurfaceKHR surface)
{
IPlatformFactory factory = Factory.Default;
if (implementation is Platform.Windows.WinGLNative win)
{
VkWin32SurfaceCreateInfoKHR createInfo = VkWin32SurfaceCreateInfoKHR.New();
createInfo.hwnd = win.Handle;
createInfo.hinstance = win.Instance;
return VK.CreateWin32SurfaceKHR(instance, ref createInfo, IntPtr.Zero, out surface);
}
else if (implementation is Platform.X11.X11GLNative x11)
{
Platform.X11.X11WindowInfo info = x11.WindowInfo as Platform.X11.X11WindowInfo;
Graphics.Vulkan.Xlib.Window window = new Graphics.Vulkan.Xlib.Window();
window.Value = info.WindowHandle;
VkXlibSurfaceCreateInfoKHR createInfo = VkXlibSurfaceCreateInfoKHR.New();
createInfo.dpy = (Graphics.Vulkan.Xlib.Display*)info.Display;
createInfo.window = window;
return VK.CreateXlibSurfaceKHR(instance, &createInfo, IntPtr.Zero, out surface);
}
else
{
//If someone knows how to create a MoltenVK surface, please add it here
throw new NotImplementedException(implementation.ToString());
}
}
public IntPtr[] RequiredVulkanExtensions()
{
if (implementation is Platform.Windows.WinGLNative win)
{
return new IntPtr[] { VulkanStrings.VK_KHR_WIN32_SURFACE_EXTENSION_NAME };
}
else if (implementation is Platform.X11.X11GLNative x11)
{
return new IntPtr[] { VulkanStrings.VK_KHR_XLIB_SURFACE_EXTENSION_NAME };
}
else
{
throw new NotImplementedException(implementation.ToString());
}
}
/// <summary>
/// System.Threading.Thread.CurrentThread.ManagedThreadId of the thread that created this <see cref="OpenTK.NativeWindow"/>.
/// </summary>

View file

@ -3,7 +3,7 @@
<AssemblyName>OpenTK</AssemblyName>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>OpenTK</RootNamespace>
<DefineConstants>$(DefineConstants);WIN32;CARBON;X11;SDL2;OPENGL;OPENGLES;MINIMAL</DefineConstants>
<DefineConstants>$(DefineConstants);WIN32;CARBON;X11;OPENGL;OPENGLES;MINIMAL</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@ -68,22 +68,24 @@
</ItemGroup>
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.0.2</Version>
<Description>The Open Toolkit library (OpenTK) is an advanced, low-level C# wrapper for OpenGL, OpenGL ES and OpenAL.
<Version>1.0.5</Version>
<Description>The Open Toolkit library (OpenTK) is an advanced, low-level C# wrapper for OpenGL, OpenGL ES, OpenAL and Vulkan.
It is suitable for games, scientific visualizations and projects that require 3d graphics, audio or compute functionality.
Features
- Create cutting-edge graphics with OpenGL 4.4 and OpenGL ES 3.0
- Create cutting-edge graphics with OpenGL 4.6, OpenGL ES 3.0 and Vulkan
- Use the .Net/Mono language of your choice: C#, F#, VB.Net, Boo, IronPython, IronRuby
- Develop faster with inline documentation and strongly-typed enumerations for all OpenGL and OpenAL functions
- Develop faster with inline documentation and strongly-typed enumerations for all OpenGL, OpenAL and Vulkan functions
This is a .NET Core-compatible version of OpenTK.
This version can be found at https://github.com/emmauss/opentk</Description>
This version can be found at https://github.com/Ryujinx/Opentk</Description>
<PackageId>OpenTK.NetStandard</PackageId>
<Authors>emmaus</Authors>
<Company>emmaus</Company>
<Product>OpenTK</Product>
<PackageProjectUrl>https://github.com/emmauss/opentk</PackageProjectUrl>
<PackageProjectUrl>https://github.com/Ryujinx/Opentk</PackageProjectUrl>
<AssemblyVersion>1.0.5.0</AssemblyVersion>
<FileVersion>1.0.5.0</FileVersion>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="$(OutputPath)..\..\..\..\Generator.Rewrite\bin\Debug\Rewrite.exe --assembly $(OutputPath)OpenTK.dll --signing-key ..\..\OpenTK.snk -debug -netstandard" Condition="$(OS) == 'Windows_NT' and $(Configuration) == 'Debug'" />
@ -107,8 +109,17 @@ This version can be found at https://github.com/emmauss/opentk</Description>
</ProjectExtensions>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Target Name="AutoGenerateBindings" BeforeTargets="CoreCompile">
<Message Text="Generating bindings." />
<Exec Command="dotnet run --project ..\vk.generator\vk.generator.csproj --out $(MSBuildThisFileDirectory)Graphics\Vulkan\Generated" />
</Target>
<Target Name="RewriteCalliStubs" AfterTargets="Build" Condition="'$(DisableCalli)' != 'true'">
<Message Text="Rewriting calli stubs." />
<Exec Command="dotnet run --project ..\vk.rewrite\vk.rewrite.csproj --vkdll $(TargetPath)" />
</Target>
</Project>

View file

@ -46,8 +46,9 @@ namespace OpenTK.Platform
Toolkit.Init();
// Create regular platform backend
if (false) { }
#if SDL2
if (Configuration.RunningOnSdl2)
else if (Configuration.RunningOnSdl2)
{
Default = new SDL2.Sdl2Factory();
}

View file

@ -45,9 +45,9 @@ namespace OpenTK.Platform.Windows
private const ExtendedWindowStyle ParentStyleEx = ExtendedWindowStyle.WindowEdge | ExtendedWindowStyle.ApplicationWindow;
private const ExtendedWindowStyle ChildStyleEx = 0;
#if NETSTANDARD
private readonly IntPtr Instance = Functions.GetModuleHandle(typeof(WinGLNative).Module.Name);
public readonly IntPtr Instance = Functions.GetModuleHandle(typeof(WinGLNative).Module.Name);
#else
private readonly IntPtr Instance = Marshal.GetHINSTANCE(typeof(WinGLNative).Module);
public readonly IntPtr Instance = Marshal.GetHINSTANCE(typeof(WinGLNative).Module);
#endif
private readonly IntPtr ClassName = Marshal.StringToHGlobalAuto(Guid.NewGuid().ToString());
private readonly WindowProcedure WindowProcedureDelegate;
@ -96,6 +96,8 @@ namespace OpenTK.Platform.Windows
private static readonly object SyncRoot = new object();
public IntPtr Handle { get => window.WindowHandle; }
public WinGLNative(int x, int y, int width, int height, string title, GameWindowFlags options, DisplayDevice device)
{
lock (SyncRoot)

View file

@ -692,10 +692,10 @@ namespace OpenTK.Platform.X11
public int backing_store;
public IntPtr backing_planes;
public IntPtr backing_pixel;
public bool save_under;
public int save_under;
public IntPtr event_mask;
public IntPtr do_not_propagate_mask;
public bool override_redirect;
public int override_redirect;
public IntPtr colormap;
public IntPtr cursor;
}
@ -719,7 +719,7 @@ namespace OpenTK.Platform.X11
public IntPtr backing_pixel;
public byte save_under;
public IntPtr colormap;
public bool map_installed;
public int map_installed;
public MapState map_state;
public IntPtr all_event_masks;
public IntPtr your_event_mask;

View file

@ -0,0 +1,252 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Vk.Generator
{
public class CodeGenerator
{
public static void GenerateCodeFiles(VulkanSpecification spec, TypeNameMappings tnm, string path)
{
if (!Directory.Exists(path))
{
}
GenerateAllTypes(spec, tnm, path);
}
private static void GenerateAllTypes(VulkanSpecification spec, TypeNameMappings tnm, string path)
{
using (StreamWriter sw = File.CreateText(Path.Combine(path, "Structures.gen.cs")))
{
CsCodeWriter cw = new CsCodeWriter(sw);
cw.WriteHeader();
cw.WriteLine();
cw.Using("System");
cw.WriteLine();
using (cw.PushBlock("namespace OpenTK.Graphics.Vulkan"))
{
Util.SpaceSeparatedList(cw, spec.Structures, structure =>
{
StructureHelpers.WriteStructure(cw, structure, tnm, spec.Constants);
});
}
}
using (StreamWriter enumWriter = File.CreateText(Path.Combine(path, "Enums.gen.cs")))
{
CsCodeWriter cw = new CsCodeWriter(enumWriter);
cw.WriteHeader();
cw.WriteLine();
cw.Using("System");
cw.WriteLine();
using (cw.PushBlock("namespace OpenTK.Graphics.Vulkan"))
{
Util.SpaceSeparatedList(cw, spec.Enums, enumDef =>
{
EnumHelpers.WriteEnum(cw, enumDef, tnm);
});
}
}
CommandDefinition[] allVariants = spec.Commands.SelectMany(cd => VariantGenerator.GenerateVariants(cd)).ToArray();
CommandDefinition[] allCommandsWithVariants = spec.Commands.Concat(allVariants).OrderBy(cd => cd.Name).ToArray();
using (StreamWriter commandWriter = File.CreateText(Path.Combine(path, "Commands.gen.cs")))
{
CsCodeWriter cw = new CsCodeWriter(commandWriter);
cw.WriteHeader();
cw.WriteLine();
cw.Using("System");
cw.Using("System.Runtime.InteropServices");
cw.WriteLine();
using (cw.PushBlock("namespace OpenTK.Graphics.Vulkan"))
using (cw.PushBlock("public static unsafe partial class VK"))
{
Util.SpaceSeparatedList(cw, allCommandsWithVariants, command =>
{
CommandHelpers.WriteCommand(cw, tnm, command);
});
cw.WriteLine();
using (cw.PushBlock("private static void LoadFunctionPointers()"))
{
foreach (CommandDefinition command in spec.Commands)
{
cw.WriteLine($"{command.Name}_ptr = s_nativeLib.LoadFunctionPointer(\"{command.RealName}\");");
}
}
}
}
using (StreamWriter handleWriter = File.CreateText(Path.Combine(path, "Handles.gen.cs")))
{
CsCodeWriter cw = new CsCodeWriter(handleWriter);
cw.WriteHeader();
cw.WriteLine();
cw.Using("System");
cw.Using("System.Diagnostics");
cw.WriteLine();
using (cw.PushBlock("namespace OpenTK.Graphics.Vulkan"))
{
Util.SpaceSeparatedList(cw, spec.Handles, handle =>
{
HandleHelpers.WriteHandle(cw, handle);
});
}
}
using (StreamWriter unionWriter = File.CreateText(Path.Combine(path, "Unions.gen.cs")))
{
CsCodeWriter cw = new CsCodeWriter(unionWriter);
cw.WriteHeader();
cw.WriteLine();
cw.Using("System.Runtime.InteropServices");
cw.WriteLine();
using (cw.PushBlock("namespace OpenTK.Graphics.Vulkan"))
{
Util.SpaceSeparatedList(cw, spec.Unions, union =>
{
UnionHelpers.WriteUnion(cw, tnm, union);
});
}
}
using (StreamWriter unionWriter = File.CreateText(Path.Combine(path, "Constants.gen.cs")))
{
CsCodeWriter cw = new CsCodeWriter(unionWriter);
cw.WriteHeader();
cw.WriteLine();
cw.Using("System.Runtime.InteropServices");
cw.WriteLine();
using (cw.PushBlock("namespace OpenTK.Graphics.Vulkan"))
{
ConstantHelpers.WriteAllConstants(cw, tnm, spec.Constants);
}
}
}
}
public class CsCodeWriter
{
private readonly StreamWriter _sw;
private int _indentLevel = 0;
public CsCodeWriter(StreamWriter sw)
{
_sw = sw;
}
public void Using(string ns)
{
_sw.WriteLine($"using {ns};");
}
public void WriteHeader()
{
_sw.WriteLine("// This file is generated.");
}
public void PushBlock()
{
WriteLine('{');
_indentLevel += 4;
}
public CodeBlock PushBlock(string ns)
{
return new CodeBlock(this, ns);
}
public IfDefSection PushIfDef(string condition)
{
return new IfDefSection(this, condition);
}
public void PopBlock()
{
_indentLevel -= 4;
WriteLine('}');
}
public void WriteLine()
{
_sw.WriteLine();
}
public void WriteLine(string text)
{
WriteIndentation();
_sw.WriteLine(text);
}
public void WriteLine(char c)
{
WriteIndentation();
_sw.WriteLine(c);
}
public void Write(string text)
{
_sw.Write(text);
}
public void WriteIndentation()
{
for (int i = 0; i < _indentLevel; i++)
{
_sw.Write(' ');
}
}
public class CodeBlock : IDisposable
{
private readonly CsCodeWriter _cw;
public CodeBlock(CsCodeWriter cw, string header)
{
_cw = cw;
_cw.WriteLine(header);
_cw.PushBlock();
}
public void Dispose()
{
_cw.PopBlock();
}
}
public class IfDefSection : IDisposable
{
private readonly CsCodeWriter _cw;
private readonly string _condition;
public IfDefSection(CsCodeWriter cw, string condition)
{
_cw = cw;
_condition = condition;
_cw.WriteLine($"#if {condition}");
}
public void Dispose()
{
_cw.WriteLine($"#endif // {_condition}");
}
}
}
}

View file

@ -0,0 +1,77 @@
using System;
using System.Linq;
using System.Xml.Linq;
namespace Vk.Generator
{
public class CommandDefinition
{
public string Name { get; }
public TypeSpec ReturnType { get; }
public ParameterDefinition[] Parameters { get; }
public string[] SuccessCodes { get; }
public string[] ErrorCodes { get; }
public bool IsVariant { get; }
public string RealName { get => "vk" + Name; }
public CommandDefinition(string name, TypeSpec returnType, ParameterDefinition[] parameters, string[] successCodes, string[] errorCodes, bool isVariant)
{
Require.NotNull(parameters);
Require.NotNull(successCodes);
Require.NotNull(errorCodes);
if (name.StartsWith("vk"))
{
name = name.Substring(2);
}
Name = name;
ReturnType = returnType;
Parameters = parameters;
SuccessCodes = successCodes;
ErrorCodes = errorCodes;
IsVariant = isVariant;
}
public static CommandDefinition CreateFromXml(XElement xe)
{
Require.Equal("command", xe.Name);
var proto = xe.Element("proto");
string name = proto.Element("name").Value;
string returnTypeName = proto.Element("type").Value;
TypeSpec returnType = new TypeSpec(returnTypeName);
var successAttr = xe.Attribute("successcodes");
string[] successCodes = successAttr != null
? successAttr.Value.Split(',').ToArray()
: Array.Empty<string>();
var errorAttr = xe.Attribute("errorcodes");
string[] errorCodes = errorAttr != null
? errorAttr.Value.Split(',').ToArray()
: Array.Empty<string>();
ParameterDefinition[] parameters = xe.Elements("param")
.Select(paramXml => ParameterDefinition.CreateFromXml(paramXml)).ToArray();
return new CommandDefinition(name, returnType, parameters, successCodes, errorCodes, false);
}
public string GetParametersSignature(TypeNameMappings tnm)
{
return string.Join(", ", Parameters.Select(pd => pd.GetMappedAndNormalizedString(tnm)));
}
public string GetParametersSignature()
{
return string.Join(", ", Parameters.Select(pd => pd.ToString()));
}
public override string ToString()
{
string paramSig = GetParametersSignature();
return $"{ReturnType} {Name}({paramSig})";
}
}
}

View file

@ -0,0 +1,27 @@
using System.Linq;
using System.Runtime.InteropServices;
namespace Vk.Generator
{
public static class CommandHelpers
{
public static void WriteCommand(CsCodeWriter cw, TypeNameMappings tnm, CommandDefinition command)
{
if (!command.IsVariant)
{
cw.WriteLine($"private static IntPtr {command.Name}_ptr;");
}
if (command.SuccessCodes.Length != 0)
{
cw.WriteLine($"///<remarks>Success codes:{string.Join(", ", command.SuccessCodes)}. Error codes:{string.Join(", ", command.ErrorCodes)}</remarks>");
}
cw.WriteLine("[Generator.CalliRewrite]");
using (cw.PushBlock($"public static unsafe {command.ReturnType.MapTypeSpec(tnm)} {command.Name}({command.GetParametersSignature(tnm)})"))
{
cw.WriteLine("throw new NotImplementedException();");
}
}
}
}

View file

@ -0,0 +1,10 @@
using System;
namespace Vk.Generator
{
public static class Configuration
{
public static string CodeOutputPath { get; set; } = AppContext.BaseDirectory;
public static bool MapBaseTypes { get; set; } = true;
}
}

View file

@ -0,0 +1,55 @@
using System;
using System.Xml.Linq;
namespace Vk.Generator
{
public class ConstantDefinition
{
public string Name { get; }
public string Value { get; }
public ConstantType Type { get; }
public string Comment { get; }
public ConstantDefinition(string name, string value, string comment)
{
Name = name;
Value = value;
Type = ParseType(value);
Comment = comment;
}
private ConstantType ParseType(string value)
{
if (value.EndsWith("f"))
{
return ConstantType.Float32;
}
else if (value.EndsWith("ULL)"))
{
return ConstantType.UInt64;
}
else
{
return ConstantType.UInt32;
}
}
public static ConstantDefinition CreateFromXml(XElement xe)
{
Require.NotNull(xe);
string name = xe.GetNameAttribute();
string value = xe.Attribute("value").Value;
string comment = xe.Attribute("comment")?.Value;
return new ConstantDefinition(name, value, comment);
}
public enum ConstantType
{
UInt32,
UInt64,
Float32,
}
}
}

View file

@ -0,0 +1,62 @@
using System;
namespace Vk.Generator
{
public static class ConstantHelpers
{
public static void WriteAllConstants(CsCodeWriter cw, TypeNameMappings tnm, ConstantDefinition[] constants)
{
using (cw.PushBlock("public static partial class VK"))
{
foreach (ConstantDefinition constant in constants)
{
if (constant.Comment != null)
{
cw.WriteLine($"///<summary>{constant.Comment}</summary>");
}
cw.WriteLine($"public const {GetCSharpNameForConstantType(constant.Type)} {EnumHelpers.GetPrettyEnumName(constant.Name, "VK_")} = {NormalizeValue(constant.Value)};");
}
}
cw.WriteLine();
using (cw.PushBlock("public static partial class RawConstants"))
{
foreach (ConstantDefinition constant in constants)
{
if (constant.Comment != null)
{
cw.WriteLine($"///<summary>{constant.Comment}</summary>");
}
cw.WriteLine($"public const {GetCSharpNameForConstantType(constant.Type)} {constant.Name} = VK.{EnumHelpers.GetPrettyEnumName(constant.Name, "VK_")};");
}
}
}
public static void WriteConstant(CsCodeWriter cw, TypeNameMappings tnm, ConstantDefinition constant)
{
}
public static string GetCSharpNameForConstantType(ConstantDefinition.ConstantType type)
{
switch (type)
{
case ConstantDefinition.ConstantType.UInt32:
return "uint";
case ConstantDefinition.ConstantType.UInt64:
return "ulong";
case ConstantDefinition.ConstantType.Float32:
return "float";
default:
throw new InvalidOperationException("Illegal ConstantType: " + type);
}
}
private static string NormalizeValue(string value)
{
return value.Replace("ULL", "UL");
}
}
}

View file

@ -0,0 +1,101 @@
using System;
using System.Linq;
using System.Xml.Linq;
namespace Vk.Generator
{
public class EnumDefinition
{
public string Name { get; }
public EnumType Type { get; }
public EnumValue[] Values { get; set; }
public EnumDefinition(string name, EnumType type, EnumValue[] values)
{
Require.NotNullOrEmpty(name);
Require.NotNull(values);
Name = name;
Type = type;
Values = values;
}
public static EnumDefinition CreateFromXml(XElement xe)
{
Require.NotNull(xe);
EnumType type;
var typeAttr = xe.Attribute("type");
if (typeAttr != null)
{
string typeString = xe.Attribute("type").Value;
type = (EnumType)Enum.Parse(typeof(EnumType), typeString, true);
}
else
{
type = EnumType.Constants;
}
string name = xe.Attribute("name").Value;
EnumValue[] values = xe.Elements("enum").Select(valuesx => EnumValue.CreateFromXml(valuesx, type == EnumType.Bitmask)).ToArray();
return new EnumDefinition(name, type, values);
}
public override string ToString()
{
return $"Enum: {Name} ({Type})[{Values.Length}]";
}
}
public enum EnumType
{
Bitmask,
Enum,
Constants,
}
public class EnumValue
{
public string Name { get; }
public int ValueOrBitPosition { get; }
public string Comment { get; }
public EnumValue(string name, int value, string comment)
{
Name = name;
ValueOrBitPosition = value;
Comment = comment;
}
public static EnumValue CreateFromXml(XElement xe, bool isBitmask)
{
Require.NotNull(xe);
string name = xe.Attribute("name").Value;
int value;
string valueStr = xe.Attribute("value")?.Value;
if (valueStr != null)
{
if (valueStr.StartsWith("0x"))
{
valueStr = valueStr.Substring(2);
value = int.Parse(valueStr, System.Globalization.NumberStyles.HexNumber);
}
else
{
value = int.Parse(valueStr);
}
}
else
{
string bitposStr = xe.Attribute("bitpos").Value;
value = 1 << int.Parse(bitposStr);
}
var commentAttr = xe.Attribute("comment");
string comment = commentAttr != null ? commentAttr.Value : string.Empty;
return new EnumValue(name, value, comment);
}
}
}

View file

@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Vk.Generator
{
public static class EnumHelpers
{
private static readonly char[] s_underscoreSeparator = { '_' };
private static readonly Dictionary<string, string> s_knownEnumPrefixes = new Dictionary<string, string>
{
{ "VkResult", "VK" },
};
private static readonly Dictionary<string, string> s_knownEnumValueNames = new Dictionary<string, string>
{
{ "VK_STENCIL_FRONT_AND_BACK", "FrontAndBack" },
// VkStructureType
// VkSampleCountFlagBits
{ "VK_SAMPLE_COUNT_1_BIT", "Count1" },
{ "VK_SAMPLE_COUNT_2_BIT", "Count2" },
{ "VK_SAMPLE_COUNT_4_BIT", "Count4" },
{ "VK_SAMPLE_COUNT_8_BIT", "Count8" },
{ "VK_SAMPLE_COUNT_16_BIT", "Count16" },
{ "VK_SAMPLE_COUNT_32_BIT", "Count32" },
{ "VK_SAMPLE_COUNT_64_BIT", "Count64" },
// VkImageType
{ "VK_IMAGE_TYPE_1D", "Image1D" },
{ "VK_IMAGE_TYPE_2D", "Image2D" },
{ "VK_IMAGE_TYPE_3D", "Image3D" },
// VkImageViewType
{ "VK_IMAGE_VIEW_TYPE_1D", "Image1D" },
{ "VK_IMAGE_VIEW_TYPE_2D", "Image2D" },
{ "VK_IMAGE_VIEW_TYPE_3D", "Image3D" },
{ "VK_IMAGE_VIEW_TYPE_CUBE", "ImageCube" },
{ "VK_IMAGE_VIEW_TYPE_1D_ARRAY", "Image1DArray" },
{ "VK_IMAGE_VIEW_TYPE_2D_ARRAY", "Image2DArray" },
{ "VK_IMAGE_VIEW_TYPE_CUBE_ARRAY", "ImageCubeArray" },
};
private static readonly HashSet<string> s_ignoredParts = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"flags",
"bit"
};
private static readonly HashSet<string> s_preserveCaps = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"khr",
"khx",
"ext",
"nv",
"nvx",
"amd",
};
public static void WriteEnum(CsCodeWriter cw, EnumDefinition enumDef, TypeNameMappings tnm)
{
if (enumDef.Type == EnumType.Bitmask)
{
cw.WriteLine("[Flags]");
}
string mappedName = tnm.GetMappedName(enumDef.Name);
string enumNamePrefix = GetEnumNamePrefix(mappedName);
using (cw.PushBlock("public enum " + mappedName))
{
if (enumDef.Type == EnumType.Bitmask && !enumDef.Values.Any(ev => GetPrettyEnumName(ev.Name, enumNamePrefix) == "None"))
{
cw.WriteLine($"None = 0,");
}
foreach (var value in enumDef.Values)
{
if (!string.IsNullOrEmpty(value.Comment))
{
cw.WriteLine($"///<summary>{value.Comment}</summary>");
}
string prettyName = GetPrettyEnumName(value.Name, enumNamePrefix);
cw.WriteLine($"{prettyName} = {value.ValueOrBitPosition},");
}
}
using (cw.PushBlock("public static partial class RawConstants"))
{
foreach (var value in enumDef.Values)
{
if (!string.IsNullOrEmpty(value.Comment))
{
cw.WriteLine($"///<summary>{value.Comment}</summary>");
}
string prettyName = GetPrettyEnumName(value.Name, enumNamePrefix);
cw.WriteLine($"public const {mappedName} {value.Name} = {mappedName}.{prettyName};");
}
}
}
public static string GetEnumNamePrefix(string typeName)
{
if (s_knownEnumPrefixes.TryGetValue(typeName, out string knownValue))
{
return knownValue;
}
List<string> parts = new List<string>(4);
int chunkStart = 0;
for (int i = 0; i < typeName.Length; i++)
{
if (char.IsUpper(typeName[i]))
{
if (chunkStart != i)
{
parts.Add(typeName.Substring(chunkStart, i - chunkStart));
}
chunkStart = i;
if (i == typeName.Length - 1)
{
parts.Add(typeName.Substring(i, 1));
}
}
else if (i == typeName.Length - 1)
{
parts.Add(typeName.Substring(chunkStart, typeName.Length - chunkStart));
}
}
for (int i = 0; i < parts.Count; i++)
{
if (
parts[i] == "Flag"
|| parts[i] == "Flags"
|| (parts[i] == "K" && (i + 2) < parts.Count && parts[i + 1] == "H" && parts[i + 2] == "R")
|| (parts[i] == "A" && (i + 2) < parts.Count && parts[i + 1] == "M" && parts[i + 2] == "D")
|| (parts[i] == "E" && (i + 2) < parts.Count && parts[i + 1] == "X" && parts[i + 2] == "T")
|| (parts[i] == "Type" && (i + 3) < parts.Count && parts[i + 1] == "N" && parts[i + 2] == "V" && parts[i + 3] == "X")
)
{
parts = new List<string>(parts.Take(i));
break;
}
}
return string.Join("_", parts.Select(s => s.ToUpper()));
}
public static string TrimIgnoredParts(string ret)
{
foreach (string ignored in s_ignoredParts)
{
int index;
if ((index = ret.IndexOf(ignored, StringComparison.OrdinalIgnoreCase)) != -1)
{
ret = ret.Remove(index, ignored.Length);
}
}
return ret;
}
public static string GetPrettyEnumName(string value, string enumPrefix)
{
if (s_knownEnumValueNames.TryGetValue(value, out string knownName))
{
return knownName;
}
if (value.IndexOf(enumPrefix) != 0)
{
return value;
}
string[] parts = value.Substring(enumPrefix.Length, value.Length - enumPrefix.Length)
.Split(s_underscoreSeparator, StringSplitOptions.RemoveEmptyEntries);
StringBuilder sb = new StringBuilder();
foreach (string part in parts)
{
if (s_ignoredParts.Contains(part))
{
continue;
}
if (s_preserveCaps.Contains(part))
{
sb.Append(part);
}
else
{
sb.Append(char.ToUpper(part[0]));
for (int i = 1; i < part.Length; i++)
{
sb.Append(char.ToLower(part[i]));
}
}
}
string prettyName = sb.ToString();
return (char.IsNumber(prettyName[0])) ? "_" + prettyName : prettyName;
}
}
}

View file

@ -0,0 +1,126 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
namespace Vk.Generator
{
public class ExtensionDefinition
{
public string Name { get; }
public int Number { get; }
public string Type { get; }
public ExtensionConstant[] Constants { get; }
public EnumExtensionValue[] EnumExtensions { get; }
public string[] CommandNames { get; }
public ExtensionDefinition(
string name,
int number,
string type,
ExtensionConstant[] constants,
EnumExtensionValue[] enumExtensions,
string[] commandNames)
{
Name = name;
Number = number;
Type = type;
Constants = constants;
EnumExtensions = enumExtensions;
CommandNames = commandNames;
}
public static ExtensionDefinition CreateFromXml(XElement xe)
{
string name = xe.GetNameAttribute();
string numberString = xe.Attribute("number").Value;
int number = int.Parse(numberString);
string type = xe.GetTypeAttributeOrNull();
List<ExtensionConstant> extensionConstants = new List<ExtensionConstant>();
List<EnumExtensionValue> enumExtensions = new List<EnumExtensionValue>();
List<string> commandNames = new List<string>();
foreach (var require in xe.Elements("require"))
{
foreach (var enumXE in require.Elements("enum"))
{
string enumName = enumXE.GetNameAttribute();
string extends = enumXE.Attribute("extends")?.Value;
if (extends != null)
{
string valueString;
string offsetString = enumXE.Attribute("offset")?.Value;
if (offsetString != null)
{
int offset = int.Parse(offsetString);
int direction = 1;
if (enumXE.Attribute("dir")?.Value == "-")
{
direction = -1;
}
int value = direction * (1000000000 + (number - 1) * 1000 + offset);
valueString = value.ToString();
}
else
{
string bitPosString = enumXE.Attribute("bitpos")?.Value;
if (bitPosString != null)
{
int shift = int.Parse(bitPosString);
valueString = (1 << shift).ToString();
}
else
{
valueString = enumXE.Attribute("value").Value;
}
}
enumExtensions.Add(new EnumExtensionValue(extends, enumName, valueString));
}
else
{
var valueAttribute = enumXE.Attribute("value");
if (valueAttribute == null)
continue;
extensionConstants.Add(new ExtensionConstant(name, valueAttribute.Value));
}
}
foreach (var commandXE in require.Elements("command"))
{
commandNames.Add(commandXE.GetNameAttribute());
}
}
return new ExtensionDefinition(name, number, type, extensionConstants.ToArray(), enumExtensions.ToArray(), commandNames.ToArray());
}
}
public class ExtensionConstant
{
public string Name { get; }
public string Value { get; }
public ExtensionConstant(string name, string value)
{
Name = name;
Value = value;
}
}
[DebuggerDisplay("{DebuggerDisplayString}")]
public class EnumExtensionValue
{
public string ExtendedType { get; }
public string Name { get; }
public string Value { get; }
public EnumExtensionValue(string extendedType, string name, string value)
{
ExtendedType = extendedType;
Name = name;
Value = value;
}
private string DebuggerDisplayString =>
$"Ext: {ExtendedType}, {Name} = {Value}";
}
}

View file

@ -0,0 +1,28 @@
using System.Xml.Linq;
namespace Vk.Generator
{
public class HandleDefinition
{
public string Name { get; }
public bool Dispatchable { get; }
public string Parent { get; }
public HandleDefinition(string name, bool dispatchable, string parent)
{
Name = name;
Dispatchable = dispatchable;
Parent = parent;
}
public static HandleDefinition CreateFromXml(XElement xe)
{
Require.NotNull(xe);
string name = xe.GetNameElement();
bool dispatchable = xe.GetTypeElement() == "VK_DEFINE_HANDLE";
string parent = xe.Attribute("parent")?.Value;
return new HandleDefinition(name, dispatchable, parent);
}
}
}

View file

@ -0,0 +1,39 @@
using System;
namespace Vk.Generator
{
public static class HandleHelpers
{
public static void WriteHandle(CsCodeWriter cw, HandleDefinition handle)
{
if (handle.Parent != null)
{
cw.WriteIndentation();
cw.Write($"///<summary>");
cw.Write($"A {(handle.Dispatchable ? "dispatchable" : "non-dispatchable")} handle owned by a {handle.Parent}.");
cw.Write("</summary>");
cw.Write(Environment.NewLine);
}
cw.WriteLine($"[DebuggerDisplay(\"{{DebuggerDisplay,nq}}\")]");
using (cw.PushBlock($"public partial struct {handle.Name} : IEquatable<{handle.Name}>"))
{
string handleType = handle.Dispatchable ? "IntPtr" : "ulong";
string nullValue = handle.Dispatchable ? "IntPtr.Zero" : "0";
cw.WriteLine($"public readonly {handleType} Handle;");
cw.WriteLine($"public {handle.Name}({handleType} existingHandle) {{ Handle = existingHandle; }}");
cw.WriteLine($"public static {handle.Name} Null => new {handle.Name}({nullValue});");
cw.WriteLine($"public static implicit operator {handle.Name}({handleType} handle) => new {handle.Name}(handle);");
cw.WriteLine($"public static bool operator ==({handle.Name} left, {handle.Name} right) => left.Handle == right.Handle;");
cw.WriteLine($"public static bool operator !=({handle.Name} left, {handle.Name} right) => left.Handle != right.Handle;");
cw.WriteLine($"public static bool operator ==({handle.Name} left, {handleType} right) => left.Handle == right;");
cw.WriteLine($"public static bool operator !=({handle.Name} left, {handleType} right) => left.Handle != right;");
cw.WriteLine($"public bool Equals({handle.Name} h) => Handle.Equals(h.Handle);");
cw.WriteLine($"public override bool Equals(object o) => o is {handle.Name} h && Equals(h);");
cw.WriteLine($"public override int GetHashCode() => Handle.GetHashCode();");
cw.WriteLine($"private string DebuggerDisplay => string.Format(\"{handle.Name} [0x{{0}}]\", Handle.ToString(\"X\"));");
}
}
}
}

View file

@ -0,0 +1,76 @@
using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace Vk.Generator
{
public class MemberSpec
{
public string Name { get; }
public TypeSpec Type { get; }
public bool IsOptional { get; }
public int ElementCount { get; }
public string ElementCountSymbolic { get; }
public string Comment { get; }
public string LegalValues { get; }
public MemberSpec(string name, TypeSpec type, bool isOptional, int elementCount, string elementCountSymbolic, string comment, string legalValues)
{
Name = name;
Type = type;
IsOptional = isOptional;
ElementCount = elementCount;
ElementCountSymbolic = elementCountSymbolic;
Comment = comment;
LegalValues = legalValues;
}
public static MemberSpec CreateFromXml(XElement xe)
{
Require.NotNull(xe);
string name = xe.GetNameElement();
bool isOptional = xe.GetOptionalAttributeOrFalse();
string typeName = xe.Element("type").Value;
int pointerLevel = xe.Value.Contains($"{typeName}*") ? 1 : 0; // TODO: Make this better.
if (xe.Value.Contains($"{typeName}* const*"))
{
pointerLevel += 1;
}
TypeSpec type = new TypeSpec(typeName, pointerLevel);
bool foundConstantElementCount = false;
int elementCount = 1;
string elementCountSymbolic = null;
for (int i = 2; i < 10; i++)
{
if (xe.Value.Contains($"{name}[{i}]"))
{
elementCount = i;
foundConstantElementCount = true;
break;
}
}
if (!foundConstantElementCount)
{
Match m = Regex.Match(xe.Value, @"\[(.*)\]");
if (m.Captures.Count > 0)
{
elementCountSymbolic = m.Groups[1].Value;
}
}
string value = xe.Attribute("values")?.Value;
return new MemberSpec(name, type, isOptional, elementCount, elementCountSymbolic, string.Empty, value);
}
public override string ToString()
{
string optionalPart = IsOptional ? "[opt] " : "";
string countPart = ElementCount != 1 ? $" [{ElementCount}]" : ElementCountSymbolic != null ? $" [{ElementCountSymbolic}]" : "";
return $"{optionalPart}{Type} {Name}";
}
}
}

View file

@ -0,0 +1,76 @@
using System;
using System.Xml.Linq;
namespace Vk.Generator
{
public class ParameterDefinition
{
public string Name { get; }
public TypeSpec Type { get; }
public ParameterModifier Modifier { get; }
public bool IsOptional { get; }
public ParameterDefinition(string name, TypeSpec type, ParameterModifier modifier, bool isOptional)
{
Name = name;
Type = type;
Modifier = modifier;
IsOptional = isOptional;
}
public static ParameterDefinition CreateFromXml(XElement xe)
{
string name = xe.Element("name").Value;
var optionalAttr = xe.Attribute("optional");
bool isOptional = optionalAttr != null && optionalAttr.Value == "true";
string typeName = xe.Element("type").Value;
int pointerLevel = 0;
if (xe.Value.Contains($"{typeName}**") || xe.Value.Contains($"{typeName}* const*"))
{
pointerLevel = 2;
}
else if(xe.Value.Contains($"{typeName}*"))
{
pointerLevel = 1;
}
TypeSpec type = new TypeSpec(typeName, pointerLevel);
return new ParameterDefinition(name, type, ParameterModifier.None, isOptional);
}
public string GetMappedAndNormalizedString(TypeNameMappings tnm)
{
return $"{GetModifierString()}{Type.MapTypeSpec(tnm)} {Util.NormalizeFieldName(Name)}";
}
public string GetModifierString()
{
if (Modifier == ParameterModifier.Ref)
{
return "ref ";
}
else if (Modifier == ParameterModifier.Out)
{
return "out ";
}
else
{
return string.Empty;
}
}
public override string ToString()
{
return $"{GetModifierString()}{Type} {Name}";
}
}
public enum ParameterModifier
{
None,
Ref,
Out
}
}

View file

@ -0,0 +1,69 @@
using System;
using System.IO;
using System.CommandLine;
using System.Collections.Generic;
namespace Vk.Generator
{
public class Program
{
public static int Main(string[] args)
{
string outputPath = AppContext.BaseDirectory;
ArgumentSyntax.Parse(args, s =>
{
s.DefineOption("o|out", ref outputPath, "The folder into which code is generated. Defaults to the application directory.");
});
Configuration.CodeOutputPath = outputPath;
if (File.Exists(outputPath))
{
Console.Error.WriteLine("The given path is a file, not a folder.");
return 1;
}
else if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
using (var fs = File.OpenRead(Path.Combine(AppContext.BaseDirectory, "vk.xml")))
{
VulkanSpecification vs = VulkanSpecification.LoadFromXmlStream(fs);
TypeNameMappings tnm = new TypeNameMappings();
foreach (var typedef in vs.Typedefs)
{
if (typedef.Requires != null)
{
tnm.AddMapping(typedef.Requires, typedef.Name);
}
else
{
tnm.AddMapping(typedef.Name, "uint");
}
}
HashSet<string> definedBaseTypes = new HashSet<string>
{
"VkBool32"
};
if (Configuration.MapBaseTypes)
{
foreach (var baseType in vs.BaseTypes)
{
if (!definedBaseTypes.Contains(baseType.Key))
{
tnm.AddMapping(baseType.Key, baseType.Value);
}
}
}
CodeGenerator.GenerateCodeFiles(vs, tnm, Configuration.CodeOutputPath);
}
return 0;
}
}
}

View file

@ -0,0 +1,32 @@
using System;
using System.Xml.Linq;
namespace Vk.Generator
{
public static class Require
{
public static void NotNull<T>(T obj) where T : class
{
if (obj == null)
{
throw new InvalidOperationException("Object should not have been null.");
}
}
public static void NotNullOrEmpty(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new InvalidOperationException("Value should not have been null or empty.");
}
}
internal static void Equal<T>(T expected, T actual) where T : IEquatable<T>
{
if (!expected.Equals(actual))
{
throw new InvalidOperationException($"Requirement not met. Expected: {expected}, Actual: {actual}");
}
}
}
}

View file

@ -0,0 +1,31 @@
using System.Linq;
using System.Xml.Linq;
namespace Vk.Generator
{
public class StructureDefinition
{
public string Name { get; }
public MemberSpec[] Members { get; }
public StructureDefinition(string name, MemberSpec[] members)
{
Name = name;
Members = members;
}
public static StructureDefinition CreateFromXml(XElement xe)
{
Require.NotNull(xe);
string name = xe.GetNameAttribute();
MemberSpec[] members = xe.Elements("member").Select(memberx => MemberSpec.CreateFromXml(memberx)).ToArray();
return new StructureDefinition(name, members);
}
public override string ToString()
{
return $"struct {Name}[{Members.Length}]";
}
}
}

View file

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Vk.Generator
{
public static class StructureHelpers
{
private static readonly HashSet<string> s_fixedCapableTypes = new HashSet<string>()
{
"byte", "short", "int", "long", "char", "sbyte", "ushort", "uint", "ulong", "float", "double"
};
public static void WriteStructure(CsCodeWriter cw, StructureDefinition structure, TypeNameMappings tnm, ConstantDefinition[] constants)
{
using (cw.PushBlock("public unsafe partial struct " + structure.Name))
{
foreach (var member in structure.Members)
{
if (member.ElementCount > 1)
{
for (int i = 0; i < member.ElementCount; i++)
{
WriteMember(cw, tnm, member, "_" + i);
}
}
else if (member.ElementCountSymbolic != null)
{
var validConstant = constants.FirstOrDefault(cd => cd.Name == member.ElementCountSymbolic);
if (validConstant != null)
WriteMemberSymbolicCount(cw, tnm, member, constants);
else
WriteMember(cw, tnm, member, string.Empty);
}
else
{
WriteMember(cw, tnm, member, string.Empty);
}
}
if (HasAnyFieldWithSpecifiedValues(structure))
{
// Add a helper property which fills in the structure type.
using (cw.PushBlock($"public static {structure.Name} New()"))
{
cw.WriteLine($"{structure.Name} ret = new {structure.Name}();");
foreach (var member in structure.Members.Where(ms => ms.LegalValues != null))
{
cw.WriteLine($"ret.{member.Name} = {member.Type}.{GetDefaultValueString(member.Type, member.LegalValues)};");
}
cw.WriteLine("return ret;");
}
}
}
}
private static string GetDefaultValueString(TypeSpec type, string legalValues)
{
string prefix = EnumHelpers.GetEnumNamePrefix(type.Name);
string prettyName = EnumHelpers.GetPrettyEnumName(legalValues, prefix);
return prettyName;
}
private static bool HasAnyFieldWithSpecifiedValues(StructureDefinition sd)
{
return sd.Members.Any(ms => ms.LegalValues != null);
}
private static void WriteMember(CsCodeWriter cw, TypeNameMappings tnm, MemberSpec member, string nameSuffix)
{
if (!string.IsNullOrEmpty(member.Comment))
{
cw.WriteLine($"///<summary>{member.Comment}</summary>");
}
cw.WriteLine($"public {member.Type.MapTypeSpec(tnm)} {Util.NormalizeFieldName(member.Name)}{nameSuffix};");
}
private static void WriteMemberSymbolicCount(CsCodeWriter cw, TypeNameMappings tnm, MemberSpec member, ConstantDefinition[] constants)
{
if (!CanUseFixed(member.Type.MapTypeSpec(tnm)))
{
int count = GetSymbolValue(member.ElementCountSymbolic, constants);
for (int i = 0; i < count; i++)
{
WriteMember(cw, tnm, member, "_" + i);
}
}
else
{
if (!string.IsNullOrEmpty(member.Comment))
{
cw.WriteLine($"///<summary>{member.Comment}</summary>");
}
string mappedSymbolicName = EnumHelpers.GetPrettyEnumName(member.ElementCountSymbolic, "VK_");
cw.WriteLine($"public fixed {member.Type.MapTypeSpec(tnm)} {Util.NormalizeFieldName(member.Name)}[(int)VK.{mappedSymbolicName}];");
}
}
private static int GetSymbolValue(string symbolName, ConstantDefinition[] constants)
{
return int.Parse(constants.Single(cd => cd.Name == symbolName).Value);
}
private static bool CanUseFixed(TypeSpec type)
{
return s_fixedCapableTypes.Contains(type.Name);
}
}
}

View file

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
namespace Vk.Generator
{
public class TypeNameMappings
{
private readonly Dictionary<string, string> _nameMappings = new Dictionary<string, string>()
{
{ "uint8_t", "byte" },
{ "uint32_t", "uint" },
{ "uint64_t", "ulong" },
{ "int32_t", "int" },
{ "int64_t", "long" },
{ "int64_t*", "long*" },
{ "char", "byte" },
{ "size_t", "UIntPtr" },
{ "DWORD", "uint" },
{ "ANativeWindow", "Android.ANativeWindow" },
{ "MirConnection", "Mir.MirConnection" },
{ "MirSurface", "Mir.MirSurface" },
{ "wl_display", "Wayland.wl_display" },
{ "wl_surface", "Wayland.wl_surface" },
{ "Display", "Xlib.Display" },
{ "Window", "Xlib.Window" },
{ "VisualID", "Xlib.VisualID" },
{ "RROutput", "IntPtr" },
{ "HINSTANCE", "Win32.HINSTANCE" },
{ "HWND", "Win32.HWND" },
{ "HANDLE", "Win32.HANDLE" },
{ "SECURITY_ATTRIBUTES", "Win32.SECURITY_ATTRIBUTES" },
{ "LPCWSTR", "IntPtr" },
{ "xcb_connection_t", "Xcb.xcb_connection_t" },
{ "xcb_window_t", "Xcb.xcb_window_t" },
{ "xcb_visualid_t", "Xcb.xcb_visualid_t" },
};
internal IEnumerable<KeyValuePair<string, string>> GetAllMappings()
{
return _nameMappings;
}
public void AddMapping(string originalName, string newName)
{
_nameMappings.Add(originalName, newName);
}
public string GetMappedName(string name)
{
if (_nameMappings.TryGetValue(name, out string mappedName))
{
return GetMappedName(mappedName);
}
else if (name.StartsWith("PFN"))
{
return "IntPtr";
}
else
{
return name;
}
}
}
}

View file

@ -0,0 +1,51 @@
using System;
namespace Vk.Generator
{
public class TypeSpec
{
public string Name { get; }
public int PointerIndirection { get; }
public int ArrayDimensions { get; }
public TypeSpec(string name) : this(name, 0, 0) { }
public TypeSpec(string name, int pointerIndirection) : this(name, pointerIndirection, 0) { }
public TypeSpec(string name, int pointerIndirection, int arrayDimensions)
{
Name = name;
PointerIndirection = pointerIndirection;
ArrayDimensions = arrayDimensions;
}
public TypeSpec MapTypeSpec(TypeNameMappings tnm)
{
return new TypeSpec(tnm.GetMappedName(Name), PointerIndirection, ArrayDimensions);
}
public override string ToString()
{
return GetFullTypeName();
}
private string GetFullTypeName()
{
return $"{Name}{new string('*', PointerIndirection)}{GetArrayPortion()}";
}
private string GetArrayPortion()
{
if (ArrayDimensions == 0)
{
return string.Empty;
}
else if (ArrayDimensions == 1)
{
return "[]";
}
else
{
return $"[{new string(',', ArrayDimensions - 1)}]";
}
}
}
}

View file

@ -0,0 +1,31 @@
using System.Xml.Linq;
namespace Vk.Generator
{
public class TypedefDefinition
{
public string Name { get; }
public string Requires { get; }
public string Type { get; }
public TypedefDefinition(string name, string requires, string type)
{
Name = name;
Requires = requires;
Type = type;
}
public static TypedefDefinition CreateFromXml(XElement xe)
{
string name = xe.GetNameElement();
string requires = xe.Attribute("requires")?.Value;
string type = xe.GetTypeElement();
return new TypedefDefinition(name, requires, type);
}
public override string ToString()
{
return $"{Name}, {Requires} -> {Type}";
}
}
}

View file

@ -0,0 +1,42 @@
using System;
namespace Vk.Generator
{
public class UnionHelpers
{
public static void WriteUnion(CsCodeWriter cw, TypeNameMappings tnm, StructureDefinition union)
{
cw.WriteLine("[StructLayout(LayoutKind.Explicit)]");
using (cw.PushBlock("public partial struct " + union.Name))
{
foreach (var member in union.Members)
{
if (member.ElementCount > 1)
{
for (int i = 0; i < member.ElementCount; i++)
{
int fieldSize = Util.GetTypeSize(member.Type.MapTypeSpec(tnm));
cw.WriteLine($"[FieldOffset({i * fieldSize})]");
WriteMember(cw, tnm, member, "_" + i);
}
}
else
{
cw.WriteLine("[FieldOffset(0)]");
WriteMember(cw, tnm, member, string.Empty);
}
}
}
}
private static void WriteMember(CsCodeWriter cw, TypeNameMappings tnm, MemberSpec member, string nameSuffix)
{
if (!string.IsNullOrEmpty(member.Comment))
{
cw.WriteLine($"///<summary>{member.Comment}</summary>");
}
cw.WriteLine($"public {member.Type.MapTypeSpec(tnm)} {Util.NormalizeFieldName(member.Name)}{nameSuffix};");
}
}
}

55
src/vk.generator/Util.cs Normal file
View file

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
namespace Vk.Generator
{
public static class Util
{
private static readonly Dictionary<string, int> s_typeSizes = new Dictionary<string, int>
{
{ "byte", 1 },
{ "uint", 4 },
{ "ulong", 4 },
{ "int", 4 },
{ "float", 4 },
};
private static readonly HashSet<string> s_keywords = new HashSet<string>
{
"object",
"event",
};
public static int GetTypeSize(TypeSpec type)
{
if (type.PointerIndirection != 0 || !s_typeSizes.TryGetValue(type.Name, out int size))
{
throw new InvalidOperationException();
}
return size;
}
public static string NormalizeFieldName(string name)
{
if (s_keywords.Contains(name))
{
return "@" + name;
}
return name;
}
public static void SpaceSeparatedList<T>(CsCodeWriter cw, IList<T> list, Action<T> action)
{
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
action(item);
if (i != list.Count - 1)
{
cw.WriteLine();
}
}
}
}
}

View file

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Vk.Generator
{
public static class VariantGenerator
{
public static CommandDefinition[] GenerateVariants(CommandDefinition cd)
{
ParameterDefinition[] parameters = cd.Parameters;
List<ParameterDefinition>[] parameterPossibilities = new List<ParameterDefinition>[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
parameterPossibilities[i] = new List<ParameterDefinition>() { parameters[i] };
parameterPossibilities[i].AddRange(GenerateVariantParameters(cd, parameters[i]));
}
ParameterDefinition[][] definitions = parameterPossibilities.Select(l => l.ToArray()).ToArray();
var combos = GetCombinations(definitions).ToArray();
CommandDefinition[] commands = combos.Skip(1).Select(pd =>
{
return new CommandDefinition(cd.Name, cd.ReturnType, pd, cd.SuccessCodes, cd.ErrorCodes, true);
}).ToArray();
return commands;
}
private static IEnumerable<ParameterDefinition[]> GetCombinations(ParameterDefinition[][] pds)
{
ParameterDefinition[] first = pds.First();
ParameterDefinition[][] rest = GetSubArray(pds, 1);
foreach (var option in first)
{
if (!rest.Any())
{
yield return new[] { option };
}
else
{
var restVariants = GetCombinations(rest);
foreach (var restVariant in restVariants)
{
yield return Prepend(option, restVariant);
}
}
}
}
private static IEnumerable<ParameterDefinition> GenerateVariantParameters(CommandDefinition cd, ParameterDefinition pd)
{
if (CanHaveVariant(pd))
{
if ((cd.Name.StartsWith("Create") || cd.Name.StartsWith("Allocate") || cd.Name.StartsWith("Get")) && pd == cd.Parameters.Last())
{
yield return OutVariant(pd);
}
else if (pd.Type.Name == "char" && pd.Type.PointerIndirection == 1)
{
yield return StringVariant(pd);
}
else
{
yield return RefVariant(pd);
yield return IntPtrVariant(pd);
if (pd.Name.EndsWith("Infos"))
{
yield return ArrayVariant(pd, 1);
}
}
}
}
private static ParameterDefinition StringVariant(ParameterDefinition pd)
{
TypeSpec typeSpec = new TypeSpec("string");
return new ParameterDefinition(pd.Name, typeSpec, ParameterModifier.None, pd.IsOptional);
}
private static ParameterDefinition ArrayVariant(ParameterDefinition pd, int dimensions)
{
TypeSpec typeSpec = new TypeSpec(pd.Type.Name, pd.Type.PointerIndirection - 1, dimensions);
return new ParameterDefinition(pd.Name, typeSpec, ParameterModifier.None, pd.IsOptional);
}
private static ParameterDefinition OutVariant(ParameterDefinition pd)
{
TypeSpec typeSpec = new TypeSpec(pd.Type.Name, pd.Type.PointerIndirection - 1);
return new ParameterDefinition(pd.Name, typeSpec, ParameterModifier.Out, pd.IsOptional);
}
private static ParameterDefinition RefVariant(ParameterDefinition pd)
{
TypeSpec typeSpec = new TypeSpec(pd.Type.Name, pd.Type.PointerIndirection - 1);
return new ParameterDefinition(pd.Name, typeSpec, ParameterModifier.Ref, pd.IsOptional);
}
private static ParameterDefinition IntPtrVariant(ParameterDefinition pd)
{
TypeSpec typeSpec = new TypeSpec(nameof(IntPtr));
return new ParameterDefinition(pd.Name, typeSpec, ParameterModifier.None, pd.IsOptional);
}
private static bool CanHaveVariant(ParameterDefinition pd)
{
return pd.Type.PointerIndirection > 0 && pd.Type.Name != "void";
}
private static T[] GetSubArray<T>(T[] array, int skip)
{
return array.Skip(skip).ToArray();
}
private static T[] Prepend<T>(T first, T[] rest)
{
T[] arr = new T[rest.Length + 1];
rest.CopyTo(arr, 1);
arr[0] = first;
return arr;
}
}
}

View file

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Vk.Generator
{
public class VulkanSpecification
{
public CommandDefinition[] Commands { get; }
public ConstantDefinition[] Constants { get; }
public TypedefDefinition[] Typedefs { get; }
public EnumDefinition[] Enums { get; }
public StructureDefinition[] Structures { get; }
public StructureDefinition[] Unions{ get; }
public HandleDefinition[] Handles { get; }
public string[] BitmaskTypes { get; }
public Dictionary<string, string> BaseTypes { get; }
public ExtensionDefinition[] Extensions { get; }
public VulkanSpecification(
CommandDefinition[] commands,
ConstantDefinition[] constants,
TypedefDefinition[] typedefs,
EnumDefinition[] enums,
StructureDefinition[] structures,
StructureDefinition[] unions,
HandleDefinition[] handles,
string[] bitmaskTypes,
Dictionary<string, string> baseTypes,
ExtensionDefinition[] extensions)
{
Commands = commands;
Constants = constants;
Typedefs = typedefs;
Enums = enums;
Structures = structures;
Unions = unions;
Handles = handles;
BitmaskTypes = bitmaskTypes;
BaseTypes = baseTypes;
Extensions = extensions;
AddExtensionEnums(Enums, Extensions);
}
public static VulkanSpecification LoadFromXmlStream(Stream specFileStream)
{
var spec = XDocument.Load(specFileStream);
var registry = spec.Element("registry");
var commands = registry.Element("commands");
CommandDefinition[] commandDefinitions = commands.Elements("command")
.Select(commandx => CommandDefinition.CreateFromXml(commandx)).ToArray();
ConstantDefinition[] constantDefinitions = registry.Elements("enums")
.Where(enumx => enumx.Attribute("name").Value == "API Constants")
.SelectMany(enumx => enumx.Elements("enum"))
.Select(enumxx => ConstantDefinition.CreateFromXml(enumxx)).ToArray();
var types = registry.Elements("types");
TypedefDefinition[] typedefDefinitions = types.Elements("type").Where(xe => xe.Value.Contains("typedef") && xe.HasCategoryAttribute("bitmask"))
.Select(xe2 => TypedefDefinition.CreateFromXml(xe2)).ToArray();
EnumDefinition[] enumDefinitions = registry.Elements("enums")
.Where(enumx => enumx.GetTypeAttributeOrNull() == "enum" || enumx.GetTypeAttributeOrNull() == "bitmask")
.Select(enumx => EnumDefinition.CreateFromXml(enumx)).ToArray();
StructureDefinition[] structures = types.Elements("type").Where(typex => typex.HasCategoryAttribute("struct"))
.Select(typex => StructureDefinition.CreateFromXml(typex)).ToArray();
StructureDefinition[] unions =
types.Elements("type")
.Where(typex => typex.HasCategoryAttribute("union"))
.Select(typex => StructureDefinition.CreateFromXml(typex)).ToArray();
HandleDefinition[] handles = types.Elements("type").Where(typex => typex.HasCategoryAttribute("handle"))
.Select(typex => HandleDefinition.CreateFromXml(typex)).ToArray();
string[] bitmaskTypes = types.Elements("type").Where(typex => typex.HasCategoryAttribute("bitmask"))
.Select(typex => typex.GetNameElement()).ToArray();
Dictionary<string, string> baseTypes = types.Elements("type").Where(typex => typex.HasCategoryAttribute("basetype"))
.ToDictionary(
typex => typex.GetNameElement(),
typex => typex.Element("type").Value);
ExtensionDefinition[] extensions = registry.Element("extensions").Elements("extension")
.Select(xe => ExtensionDefinition.CreateFromXml(xe)).ToArray();
return new VulkanSpecification(
commandDefinitions,
constantDefinitions,
typedefDefinitions,
enumDefinitions,
structures,
unions,
handles,
bitmaskTypes,
baseTypes,
extensions);
}
private void AddExtensionEnums(EnumDefinition[] enums, ExtensionDefinition[] extensions)
{
foreach (ExtensionDefinition exDef in extensions)
{
if (exDef.Name == "VK_KHX_device_group")
{
}
foreach (var enumEx in exDef.EnumExtensions)
{
EnumDefinition enumDef = GetEnumDef(enums, enumEx.ExtendedType);
int value = int.Parse(enumEx.Value);
enumDef.Values = enumDef.Values.Append(new EnumValue(enumEx.Name, value, null)).ToArray();
}
}
}
private EnumDefinition GetEnumDef(EnumDefinition[] enums, string name)
{
return enums.Single(ed => ed.Name == name);
}
}
}

View file

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Vk.Generator
{
public static class XElementExtensions
{
public static string GetNameAttribute(this XElement xe)
{
return xe.Attribute("name").Value;
}
public static string GetNameElement(this XElement xe)
{
return xe.Element("name").Value;
}
public static string GetTypeElement(this XElement xe)
{
return xe.Element("type").Value;
}
public static string GetTypeAttributeOrNull(this XElement xe)
{
return xe.Attribute("type")?.Value;
}
public static bool GetOptionalAttributeOrFalse(this XElement xe)
{
var attr = xe.Attribute("optional");
return attr != null
? attr.Value == "true"
: false;
}
public static bool HasCategoryAttribute(this XElement xe, string value)
{
var attr = xe.Attribute("category");
return attr != null && attr.Value == value;
}
}
}

View file

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<OutputType>Exe</OutputType>
<AssemblyName>vk.generator</AssemblyName>
<RootNamespace>Vk.Generator</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<Content Include="vk.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<SubType>Designer</SubType>
</Content>
<PackageReference Include="System.CommandLine" Version="0.1.0-e160908-1" />
</ItemGroup>
</Project>

7634
src/vk.generator/vk.xml Normal file

File diff suppressed because it is too large Load diff

201
src/vk.rewrite/Program.cs Normal file
View file

@ -0,0 +1,201 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Linq;
using Mono.Collections.Generic;
using System.IO;
using System.Collections.Generic;
using System.Text;
namespace Vk.Rewrite
{
public class Program
{
private static TypeReference s_calliRewriteRef;
private static MethodReference s_stringToHGlobalUtf8Ref;
private static MethodDefinition s_freeHGlobalRef;
private static TypeReference s_stringHandleRef;
public static int Main(string[] args)
{
string vkDllPath = null;
string outputPath = null;
bool copiedToTemp = false;
var s = System.CommandLine.ArgumentSyntax.Parse(args, syntax =>
{
syntax.DefineOption("vkdll", ref vkDllPath, "The location of vk.dll to rewrite.");
syntax.DefineOption("out", ref outputPath, "The output location of the rewritten DLL. If not specified, the DLL is rewritten in-place.");
});
if (vkDllPath == null)
{
Console.WriteLine("Error: a path for --vkdll is required.");
Console.WriteLine(s.GetHelpText());
return -1;
}
if (outputPath == null)
{
outputPath = vkDllPath;
string copyPath = Path.GetTempFileName();
File.Copy(vkDllPath, copyPath, overwrite: true);
vkDllPath = copyPath;
copiedToTemp = true;
}
try
{
Rewrite(vkDllPath, outputPath);
}
finally
{
if (copiedToTemp)
{
File.Delete(vkDllPath);
}
}
return 0;
}
private static void Rewrite(string vkDllPath, string outputPath)
{
using (AssemblyDefinition vkDll = AssemblyDefinition.ReadAssembly(vkDllPath))
{
LoadRefs(vkDll);
ModuleDefinition mainModule = vkDll.Modules[0];
s_stringHandleRef = mainModule.GetType("OpenTK.Graphics.Vulkan.StringHandle");
TypeDefinition bindingHelpers = mainModule.GetType("OpenTK.Graphics.Vulkan.BindingsHelpers");
s_stringToHGlobalUtf8Ref = bindingHelpers.Methods.Single(md => md.Name == "StringToHGlobalUtf8");
s_freeHGlobalRef = bindingHelpers.Methods.Single(md => md.Name == "FreeHGlobal");
foreach (var type in mainModule.Types)
{
ProcessType(type);
}
vkDll.Write(outputPath);
}
}
private static void LoadRefs(AssemblyDefinition vkDll)
{
s_calliRewriteRef = vkDll.MainModule.GetType("OpenTK.Graphics.Vulkan.Generator.CalliRewriteAttribute");
}
private static void ProcessType(TypeDefinition type)
{
foreach (var method in type.Methods)
{
ProcessMethod(method);
}
}
private static void ProcessMethod(MethodDefinition method)
{
if (method.CustomAttributes.Any(ca => ca.AttributeType == s_calliRewriteRef))
{
var processor = method.Body.GetILProcessor();
RewriteMethod(method);
method.CustomAttributes.Remove(method.CustomAttributes.Single(ca => ca.AttributeType == s_calliRewriteRef));
}
}
private static void RewriteMethod(MethodDefinition method)
{
var il = method.Body.GetILProcessor();
il.Body.Instructions.Clear();
List<VariableDefinition> stringParams = new List<VariableDefinition>();
for (int i = 0; i < method.Parameters.Count; i++)
{
EmitLoadArgument(il, i, method.Parameters);
TypeReference parameterType = method.Parameters[i].ParameterType;
if (parameterType.FullName == "System.String")
{
VariableDefinition variableDef = new VariableDefinition(s_stringHandleRef);
method.Body.Variables.Add(variableDef);
il.Emit(OpCodes.Call, s_stringToHGlobalUtf8Ref);
il.Emit(OpCodes.Stloc, variableDef);
il.Emit(OpCodes.Ldloc, variableDef);
stringParams.Add(variableDef);
}
else if (parameterType.IsByReference)
{
VariableDefinition byRefVariable = new VariableDefinition(new PinnedType(parameterType));
method.Body.Variables.Add(byRefVariable);
il.Emit(OpCodes.Stloc, byRefVariable);
il.Emit(OpCodes.Ldloc, byRefVariable);
il.Emit(OpCodes.Conv_I);
}
}
string functionPtrName = method.Name + "_ptr";
var field = method.DeclaringType.Fields.SingleOrDefault(fd => fd.Name == functionPtrName);
if (field == null)
{
throw new InvalidOperationException("Can't find function pointer field for " + method.Name);
}
il.Emit(OpCodes.Ldsfld, field);
CallSite callSite = new CallSite(method.ReturnType)
{
CallingConvention = MethodCallingConvention.StdCall
};
foreach (ParameterDefinition pd in method.Parameters)
{
TypeReference parameterType;
if (pd.ParameterType.IsByReference)
{
parameterType = new PointerType(pd.ParameterType.GetElementType());
}
else if (pd.ParameterType.FullName == "System.String")
{
parameterType = s_stringHandleRef;
}
else
{
parameterType = pd.ParameterType;
}
ParameterDefinition calliPD = new ParameterDefinition(pd.Name, pd.Attributes, parameterType);
callSite.Parameters.Add(calliPD);
}
il.Emit(OpCodes.Calli, callSite);
foreach (var stringVar in stringParams)
{
il.Emit(OpCodes.Ldloc, stringVar);
il.Emit(OpCodes.Call, s_freeHGlobalRef);
}
il.Emit(OpCodes.Ret);
if (method.Body.Variables.Count > 0)
{
method.Body.InitLocals = true;
}
}
private static void EmitLoadArgument(ILProcessor il, int i, Collection<ParameterDefinition> parameters)
{
if (i == 0)
{
il.Emit(OpCodes.Ldarg_0);
}
else if (i == 1)
{
il.Emit(OpCodes.Ldarg_1);
}
else if (i == 2)
{
il.Emit(OpCodes.Ldarg_2);
}
else if (i == 3)
{
il.Emit(OpCodes.Ldarg_3);
}
else
{
il.Emit(OpCodes.Ldarg, i);
}
}
}
}

View file

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk" DefaultTargets="Build;Publish">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<OutputType>Exe</OutputType>
<AssemblyName>vk.rewrite</AssemblyName>
<RootNamespace>Vk.Generator.Rewrite</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.CommandLine" Version="0.1.0-e160908-1" />
<PackageReference Include="Mono.Cecil" Version="0.10.0-beta7" />
</ItemGroup>
</Project>