Vulkan bindings (#1)

* Initial Vulkan support
This commit is contained in:
ReinUsesLisp 2018-07-31 23:06:22 -03:00 committed by emmauss
parent a5c9ddb1f4
commit 2d77d0949f
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 EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK", "src\OpenTK\OpenTK.csproj", "{A37A7E14-0000-0000-0000-000000000000}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK", "src\OpenTK\OpenTK.csproj", "{A37A7E14-0000-0000-0000-000000000000}"
EndProject 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}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK.GLControl", "src\OpenTK.GLControl\OpenTK.GLControl.csproj", "{A625BE88-0000-0000-0000-000000000000}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK.GLWidget", "src\OpenTK.GLWidget\OpenTK.GLWidget.csproj", "{A625BE87-0000-0000-0000-000000000000}" 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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTK.Standard", "src\OpenTK\OpenTK.Standard.csproj", "{67F02FD3-8F7F-4D89-8551-359993271CA3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTK.Standard", "src\OpenTK\OpenTK.Standard.csproj", "{67F02FD3-8F7F-4D89-8551-359993271CA3}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{A37A7E14-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{A625BE88-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = 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 {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}.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.ActiveCfg = Release|Any CPU
{67F02FD3-8F7F-4D89-8551-359993271CA3}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -1,6 +1,8 @@
OpenTK 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 ### MAINTAINERS WANTED

View file

@ -164,9 +164,9 @@ namespace OpenTK
return t != null; return t != null;
} }
#if SDL2
private static bool DetectSdl2() private static bool DetectSdl2()
{ {
#if SDL2
bool supported = false; bool supported = false;
// Detect whether SDL2 is supported // Detect whether SDL2 is supported
@ -220,8 +220,10 @@ namespace OpenTK
} }
return supported; return supported;
#else
return false;
#endif
} }
#endif
private static void DetectUnix(out bool unix, out bool linux, out bool macos) private static void DetectUnix(out bool unix, out bool linux, out bool macos)
{ {

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; using System.Drawing;
#endif #endif
using OpenTK.Graphics; using OpenTK.Graphics;
using OpenTK.Graphics.Vulkan;
using OpenTK.Input; using OpenTK.Input;
using OpenTK.Platform; using OpenTK.Platform;
@ -49,6 +50,55 @@ namespace OpenTK
private bool events; private bool events;
private bool previous_cursor_visible = true; 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> /// <summary>
/// System.Threading.Thread.CurrentThread.ManagedThreadId of the thread that created this <see cref="OpenTK.NativeWindow"/>. /// System.Threading.Thread.CurrentThread.ManagedThreadId of the thread that created this <see cref="OpenTK.NativeWindow"/>.
/// </summary> /// </summary>

View file

@ -3,7 +3,7 @@
<AssemblyName>OpenTK</AssemblyName> <AssemblyName>OpenTK</AssemblyName>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>OpenTK</RootNamespace> <RootNamespace>OpenTK</RootNamespace>
<DefineConstants>$(DefineConstants);WIN32;CARBON;X11;SDL2;OPENGL;OPENGLES;MINIMAL</DefineConstants> <DefineConstants>$(DefineConstants);WIN32;CARBON;X11;OPENGL;OPENGLES;MINIMAL</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@ -68,22 +68,24 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.0.2</Version> <Version>1.0.5</Version>
<Description>The Open Toolkit library (OpenTK) is an advanced, low-level C# wrapper for OpenGL, OpenGL ES and OpenAL. <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. It is suitable for games, scientific visualizations and projects that require 3d graphics, audio or compute functionality.
Features 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 - 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 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> <PackageId>OpenTK.NetStandard</PackageId>
<Authors>emmaus</Authors> <Authors>emmaus</Authors>
<Company>emmaus</Company> <Company>emmaus</Company>
<Product>OpenTK</Product> <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> </PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <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'" /> <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> </ProjectExtensions>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="4.5.0" /> <PackageReference Include="System.Drawing.Common" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Properties\" /> <Folder Include="Properties\" />
</ItemGroup> </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> </Project>

View file

@ -46,8 +46,9 @@ namespace OpenTK.Platform
Toolkit.Init(); Toolkit.Init();
// Create regular platform backend // Create regular platform backend
if (false) { }
#if SDL2 #if SDL2
if (Configuration.RunningOnSdl2) else if (Configuration.RunningOnSdl2)
{ {
Default = new SDL2.Sdl2Factory(); 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 ParentStyleEx = ExtendedWindowStyle.WindowEdge | ExtendedWindowStyle.ApplicationWindow;
private const ExtendedWindowStyle ChildStyleEx = 0; private const ExtendedWindowStyle ChildStyleEx = 0;
#if NETSTANDARD #if NETSTANDARD
private readonly IntPtr Instance = Functions.GetModuleHandle(typeof(WinGLNative).Module.Name); public readonly IntPtr Instance = Functions.GetModuleHandle(typeof(WinGLNative).Module.Name);
#else #else
private readonly IntPtr Instance = Marshal.GetHINSTANCE(typeof(WinGLNative).Module); public readonly IntPtr Instance = Marshal.GetHINSTANCE(typeof(WinGLNative).Module);
#endif #endif
private readonly IntPtr ClassName = Marshal.StringToHGlobalAuto(Guid.NewGuid().ToString()); private readonly IntPtr ClassName = Marshal.StringToHGlobalAuto(Guid.NewGuid().ToString());
private readonly WindowProcedure WindowProcedureDelegate; private readonly WindowProcedure WindowProcedureDelegate;
@ -96,6 +96,8 @@ namespace OpenTK.Platform.Windows
private static readonly object SyncRoot = new object(); 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) public WinGLNative(int x, int y, int width, int height, string title, GameWindowFlags options, DisplayDevice device)
{ {
lock (SyncRoot) lock (SyncRoot)

View file

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