diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index cb79bf219a..f37e0f8701 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -90,7 +90,6 @@ namespace ControlCatalog.Pages private int _vertexBufferObject; private int _indexBufferObject; private int _vertexArrayObject; - private GlExtrasInterface _glExt; private string GetShader(bool fragment, string shader) { @@ -258,7 +257,6 @@ namespace ControlCatalog.Pages protected unsafe override void OnOpenGlInit(GlInterface GL, int fb) { CheckError(GL); - _glExt = new GlExtrasInterface(GL); Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}"; @@ -298,8 +296,8 @@ namespace ControlCatalog.Pages GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata), GL_STATIC_DRAW); CheckError(GL); - _vertexArrayObject = _glExt.GenVertexArray(); - _glExt.BindVertexArray(_vertexArrayObject); + _vertexArrayObject = GL.GenVertexArray(); + GL.BindVertexArray(_vertexArrayObject); CheckError(GL); GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT, 0, vertexSize, IntPtr.Zero); @@ -316,12 +314,13 @@ namespace ControlCatalog.Pages // Unbind everything GL.BindBuffer(GL_ARRAY_BUFFER, 0); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - _glExt.BindVertexArray(0); + GL.BindVertexArray(0); GL.UseProgram(0); // Delete all resources. - GL.DeleteBuffers(2, new[] { _vertexBufferObject, _indexBufferObject }); - _glExt.DeleteVertexArrays(1, new[] { _vertexArrayObject }); + GL.DeleteBuffer(_vertexBufferObject); + GL.DeleteBuffer(_indexBufferObject); + GL.DeleteVertexArray(_vertexArrayObject); GL.DeleteProgram(_shaderProgram); GL.DeleteShader(_fragmentShader); GL.DeleteShader(_vertexShader); @@ -338,7 +337,7 @@ namespace ControlCatalog.Pages GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); - _glExt.BindVertexArray(_vertexArrayObject); + GL.BindVertexArray(_vertexArrayObject); GL.UseProgram(_shaderProgram); CheckError(GL); var projection = @@ -369,34 +368,5 @@ namespace ControlCatalog.Pages if (_disco > 0.01) Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); } - - class GlExtrasInterface : GlInterfaceBase - { - public GlExtrasInterface(GlInterface gl) : base(gl.GetProcAddress, gl.ContextInfo) - { - } - - public delegate void GlDeleteVertexArrays(int count, int[] buffers); - [GlMinVersionEntryPoint("glDeleteVertexArrays", 3,0)] - [GlExtensionEntryPoint("glDeleteVertexArraysOES", "GL_OES_vertex_array_object")] - public GlDeleteVertexArrays DeleteVertexArrays { get; } - - public delegate void GlBindVertexArray(int array); - [GlMinVersionEntryPoint("glBindVertexArray", 3,0)] - [GlExtensionEntryPoint("glBindVertexArrayOES", "GL_OES_vertex_array_object")] - public GlBindVertexArray BindVertexArray { get; } - public delegate void GlGenVertexArrays(int n, int[] rv); - - [GlMinVersionEntryPoint("glGenVertexArrays",3,0)] - [GlExtensionEntryPoint("glGenVertexArraysOES", "GL_OES_vertex_array_object")] - public GlGenVertexArrays GenVertexArrays { get; } - - public int GenVertexArray() - { - var rv = new int[1]; - GenVertexArrays(1, rv); - return rv[0]; - } - } } } diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs index 94e7728d68..3a8fdb8491 100644 --- a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs +++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs @@ -21,7 +21,7 @@ namespace Avalonia.OpenGL.Angle var display = IntPtr.Zero; AngleOptions.PlatformApi angleApi = default; { - if (_egl.GetPlatformDisplayEXT == null) + if (!_egl.IsGetPlatformDisplayExtAvailable) throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll"); var allowedApis = AvaloniaLocator.Current.GetService()?.AllowedPlatformApis @@ -37,7 +37,7 @@ namespace Avalonia.OpenGL.Angle else continue; - display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero, + display = _egl.GetPlatformDisplayExt(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero, new[] { EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE }); if (display != IntPtr.Zero) { diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj index 76924d060f..bacb10c792 100644 --- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj +++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj @@ -11,4 +11,5 @@ + diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs index b3469c212b..68442c1fd3 100644 --- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs +++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs @@ -66,11 +66,9 @@ namespace Avalonia.OpenGL.Controls return; gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer); - if (_depthBuffer != 0) gl.DeleteRenderbuffers(1, new[] { _depthBuffer }); + if (_depthBuffer != 0) gl.DeleteRenderbuffer(_depthBuffer); - var oneArr = new int[1]; - gl.GenRenderbuffers(1, oneArr); - _depthBuffer = oneArr[0]; + _depthBuffer = gl.GenRenderbuffer(); gl.BindRenderbuffer(GL_RENDERBUFFER, _depthBuffer); gl.RenderbufferStorage(GL_RENDERBUFFER, GlVersion.Type == GlProfileType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT, @@ -88,9 +86,9 @@ namespace Avalonia.OpenGL.Controls var gl = _context.GlInterface; gl.BindTexture(GL_TEXTURE_2D, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0); - gl.DeleteFramebuffers(1, new[] { _fb }); + gl.DeleteFramebuffer(_fb); _fb = 0; - gl.DeleteRenderbuffers(1, new[] { _depthBuffer }); + gl.DeleteRenderbuffer(_depthBuffer); _depthBuffer = 0; _attachment?.Dispose(); _attachment = null; @@ -174,9 +172,7 @@ namespace Avalonia.OpenGL.Controls { _depthBufferSize = GetPixelSize(); var gl = _context.GlInterface; - var oneArr = new int[1]; - gl.GenFramebuffers(1, oneArr); - _fb = oneArr[0]; + _fb = gl.GenFramebuffer(); gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); EnsureDepthBufferAttachment(gl); diff --git a/src/Avalonia.OpenGL/Egl/EglContext.cs b/src/Avalonia.OpenGL/Egl/EglContext.cs index bc517c3e27..e7b5dc7c83 100644 --- a/src/Avalonia.OpenGL/Egl/EglContext.cs +++ b/src/Avalonia.OpenGL/Egl/EglContext.cs @@ -24,7 +24,7 @@ namespace Avalonia.OpenGL.Egl SampleCount = sampleCount; StencilSize = stencilSize; using (MakeCurrent()) - GlInterface = GlInterface.FromNativeUtf8GetProcAddress(version, b => _egl.GetProcAddress(b)); + GlInterface = GlInterface.FromNativeUtf8GetProcAddress(version, _egl.GetProcAddress); } public IntPtr Context { get; } diff --git a/src/Avalonia.OpenGL/Egl/EglDisplay.cs b/src/Avalonia.OpenGL/Egl/EglDisplay.cs index 623364866b..d3d85e8c83 100644 --- a/src/Avalonia.OpenGL/Egl/EglDisplay.cs +++ b/src/Avalonia.OpenGL/Egl/EglDisplay.cs @@ -34,9 +34,9 @@ namespace Avalonia.OpenGL.Egl } else { - if (egl.GetPlatformDisplayEXT == null) + if (!egl.IsGetPlatformDisplayExtAvailable) throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl"); - display = egl.GetPlatformDisplayEXT(platformType, platformDisplay, attrs); + display = egl.GetPlatformDisplayExt(platformType, platformDisplay, attrs); } if (display == IntPtr.Zero) diff --git a/src/Avalonia.OpenGL/Egl/EglInterface.cs b/src/Avalonia.OpenGL/Egl/EglInterface.cs index 6148e58440..4fec8e5356 100644 --- a/src/Avalonia.OpenGL/Egl/EglInterface.cs +++ b/src/Avalonia.OpenGL/Egl/EglInterface.cs @@ -2,31 +2,26 @@ using System.Runtime.InteropServices; using Avalonia.Platform; using Avalonia.Platform.Interop; +using Avalonia.SourceGenerator; namespace Avalonia.OpenGL.Egl { - public class EglInterface : GlInterfaceBase + public unsafe partial class EglInterface { - public EglInterface() : base(Load()) + public EglInterface(Func getProcAddress) { - - } - - public EglInterface(Func getProcAddress) : base(getProcAddress) - { - + Initialize(getProcAddress); } - public EglInterface(Func getProcAddress) : base(getProcAddress) + public EglInterface(string library) : this(Load(library)) { - } - - public EglInterface(string library) : base(Load(library)) + + public EglInterface() : this(Load()) { + } - static Func Load() { var os = AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem; @@ -46,119 +41,75 @@ namespace Avalonia.OpenGL.Egl } // ReSharper disable UnassignedGetOnlyAutoProperty - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate int EglGetError(); - [GlEntryPoint("eglGetError")] - public EglGetError GetError { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglGetDisplay(IntPtr nativeDisplay); - [GlEntryPoint("eglGetDisplay")] - public EglGetDisplay GetDisplay { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglGetPlatformDisplayEXT(int platform, IntPtr nativeDisplay, int[] attrs); - [GlEntryPoint("eglGetPlatformDisplayEXT")] - [GlOptionalEntryPoint] - public EglGetPlatformDisplayEXT GetPlatformDisplayEXT { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglInitialize(IntPtr display, out int major, out int minor); - [GlEntryPoint("eglInitialize")] - public EglInitialize Initialize { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglGetProcAddress(Utf8Buffer proc); - [GlEntryPoint("eglGetProcAddress")] - public EglGetProcAddress GetProcAddress { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglBindApi(int api); - [GlEntryPoint("eglBindAPI")] - public EglBindApi BindApi { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglChooseConfig(IntPtr display, int[] attribs, - out IntPtr surfaceConfig, int numConfigs, out int choosenConfig); - [GlEntryPoint("eglChooseConfig")] - public EglChooseConfig ChooseConfig { get; } + + [GetProcAddress("eglGetError")] + public partial int GetError(); + + [GetProcAddress("eglGetDisplay")] + public partial IntPtr GetDisplay(IntPtr nativeDisplay); + + [GetProcAddress("eglGetPlatformDisplayEXT", true)] + public partial IntPtr GetPlatformDisplayExt(int platform, IntPtr nativeDisplay, int[] attrs); + + [GetProcAddress("eglInitialize")] + public partial bool Initialize(IntPtr display, out int major, out int minor); + + [GetProcAddress("eglGetProcAddress")] + public partial IntPtr GetProcAddress(IntPtr proc); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglCreateContext(IntPtr display, IntPtr config, + [GetProcAddress("eglBindAPI")] + public partial bool BindApi(int api); + + [GetProcAddress("eglChooseConfig")] + public partial bool ChooseConfig(IntPtr display, int[] attribs, + out IntPtr surfaceConfig, int numConfigs, out int choosenConfig); + + [GetProcAddress("eglCreateContext")] + public partial IntPtr CreateContext(IntPtr display, IntPtr config, IntPtr share, int[] attrs); - [GlEntryPoint("eglCreateContext")] - public EglCreateContext CreateContext { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglDestroyContext(IntPtr display, IntPtr context); - [GlEntryPoint("eglDestroyContext")] - public EglDestroyContext DestroyContext { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglCreatePBufferSurface(IntPtr display, IntPtr config, int[] attrs); - [GlEntryPoint("eglCreatePbufferSurface")] - public EglCreatePBufferSurface CreatePBufferSurface { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglMakeCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context); - [GlEntryPoint("eglMakeCurrent")] - public EglMakeCurrent MakeCurrent { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglGetCurrentContext(); - [GlEntryPoint("eglGetCurrentContext")] - public EglGetCurrentContext GetCurrentContext { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglGetCurrentDisplay(); - [GlEntryPoint("eglGetCurrentDisplay")] - public EglGetCurrentContext GetCurrentDisplay { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglGetCurrentSurface(int readDraw); - [GlEntryPoint("eglGetCurrentSurface")] - public EglGetCurrentSurface GetCurrentSurface { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void EglDisplaySurfaceVoidDelegate(IntPtr display, IntPtr surface); - [GlEntryPoint("eglDestroySurface")] - public EglDisplaySurfaceVoidDelegate DestroySurface { get; } - - [GlEntryPoint("eglSwapBuffers")] - public EglDisplaySurfaceVoidDelegate SwapBuffers { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr - EglCreateWindowSurface(IntPtr display, IntPtr config, IntPtr window, int[] attrs); - [GlEntryPoint("eglCreateWindowSurface")] - public EglCreateWindowSurface CreateWindowSurface { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglGetConfigAttrib(IntPtr display, IntPtr config, int attr, out int rv); - [GlEntryPoint("eglGetConfigAttrib")] - public EglGetConfigAttrib GetConfigAttrib { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglWaitGL(); - [GlEntryPoint("eglWaitGL")] - public EglWaitGL WaitGL { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglWaitClient(); - [GlEntryPoint("eglWaitClient")] - public EglWaitGL WaitClient { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglWaitNative(int engine); - [GlEntryPoint("eglWaitNative")] - public EglWaitNative WaitNative { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglQueryString(IntPtr display, int i); - - [GlEntryPoint("eglQueryString")] - public EglQueryString QueryStringNative { get; } + + [GetProcAddress("eglDestroyContext")] + public partial bool DestroyContext(IntPtr display, IntPtr context); + + [GetProcAddress("eglCreatePbufferSurface")] + public partial IntPtr CreatePBufferSurface(IntPtr display, IntPtr config, int[] attrs); + [GetProcAddress("eglMakeCurrent")] + public partial bool MakeCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context); + + [GetProcAddress("eglGetCurrentContext")] + public partial IntPtr GetCurrentContext(); + + [GetProcAddress("eglGetCurrentDisplay")] + public partial IntPtr GetCurrentDisplay(); + + [GetProcAddress("eglGetCurrentSurface")] + public partial IntPtr GetCurrentSurface(int readDraw); + + [GetProcAddress("eglDestroySurface")] + public partial void DestroySurface(IntPtr display, IntPtr surface); + + [GetProcAddress("eglSwapBuffers")] + public partial void SwapBuffers(IntPtr display, IntPtr surface); + + [GetProcAddress("eglCreateWindowSurface")] + public partial IntPtr CreateWindowSurface(IntPtr display, IntPtr config, IntPtr window, int[] attrs); + + [GetProcAddress("eglGetConfigAttrib")] + public partial bool GetConfigAttrib(IntPtr display, IntPtr config, int attr, out int rv); + + [GetProcAddress("eglWaitGL")] + public partial bool WaitGL(); + + [GetProcAddress("eglWaitClient")] + public partial bool WaitClient(); + + [GetProcAddress("eglWaitNative")] + public partial bool WaitNative(int engine); + + [GetProcAddress("eglQueryString")] + public partial IntPtr QueryStringNative(IntPtr display, int i); + public string QueryString(IntPtr display, int i) { var rv = QueryStringNative(display, i); @@ -166,25 +117,15 @@ namespace Avalonia.OpenGL.Egl return null; return Marshal.PtrToStringAnsi(rv); } + + [GetProcAddress("eglCreatePbufferFromClientBuffer")] + public partial IntPtr CreatePbufferFromClientBuffer(IntPtr display, int buftype, IntPtr buffer, IntPtr config, int[] attrib_list); + + [GetProcAddress("eglQueryDisplayAttribEXT", true)] + public partial bool QueryDisplayAttribExt(IntPtr display, int attr, out IntPtr res); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr EglCreatePbufferFromClientBuffer(IntPtr display, int buftype, IntPtr buffer, IntPtr config, int[] attrib_list); - [GlEntryPoint("eglCreatePbufferFromClientBuffer")] - - public EglCreatePbufferFromClientBuffer CreatePbufferFromClientBuffer { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglQueryDisplayAttribEXT(IntPtr display, int attr, out IntPtr res); - - [GlEntryPoint("eglQueryDisplayAttribEXT"), GlOptionalEntryPoint] - public EglQueryDisplayAttribEXT QueryDisplayAttribExt { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate bool EglQueryDeviceAttribEXT(IntPtr display, int attr, out IntPtr res); - - [GlEntryPoint("eglQueryDeviceAttribEXT"), GlOptionalEntryPoint] - public EglQueryDisplayAttribEXT QueryDeviceAttribExt { get; } - - // ReSharper restore UnassignedGetOnlyAutoProperty + + [GetProcAddress("eglQueryDeviceAttribEXT", true)] + public partial bool QueryDeviceAttribExt(IntPtr display, int attr, out IntPtr res); } } diff --git a/src/Avalonia.OpenGL/GlBasicInfoInterface.cs b/src/Avalonia.OpenGL/GlBasicInfoInterface.cs index aaba2ec09c..7a7110f15b 100644 --- a/src/Avalonia.OpenGL/GlBasicInfoInterface.cs +++ b/src/Avalonia.OpenGL/GlBasicInfoInterface.cs @@ -3,46 +3,24 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using Avalonia.Platform.Interop; +using Avalonia.SourceGenerator; namespace Avalonia.OpenGL { - public class GlBasicInfoInterface : GlBasicInfoInterface + public unsafe partial class GlBasicInfoInterface { - public GlBasicInfoInterface(Func getProcAddress) : base(getProcAddress, null) - { - } - - public GlBasicInfoInterface(Func nativeGetProcAddress) : base(nativeGetProcAddress, null) - { + public GlBasicInfoInterface(Func getProcAddress){ + Initialize(getProcAddress); } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGetIntegerv(int name, out int rv); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr GlGetString(int v); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr GlGetStringi(int v, int v1); - } - public class GlBasicInfoInterface : GlInterfaceBase - { - public GlBasicInfoInterface(Func getProcAddress, TContextInfo context) : base(getProcAddress, context) - { - } + [GetProcAddress("glGetIntegerv")] + public partial void GetIntegerv(int name, out int rv); - public GlBasicInfoInterface(Func nativeGetProcAddress, TContextInfo context) : base(nativeGetProcAddress, context) - { - } - - [GlEntryPoint("glGetIntegerv")] - public GlBasicInfoInterface.GlGetIntegerv GetIntegerv { get; } - - - [GlEntryPoint("glGetString")] - public GlBasicInfoInterface.GlGetString GetStringNative { get; } - - [GlEntryPoint("glGetStringi")] - public GlBasicInfoInterface.GlGetStringi GetStringiNative { get; } + [GetProcAddress("glGetString")] + public partial IntPtr GetStringNative(int v); + + [GetProcAddress("glGetStringi")] + public partial IntPtr GetStringiNative(int v, int v1); public string GetString(int v) { diff --git a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs index 2ffdaca8fe..3e31de6995 100644 --- a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs +++ b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs @@ -2,117 +2,54 @@ using System; namespace Avalonia.OpenGL { - public interface IGlEntryPointAttribute + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + class GlMinVersionEntryPoint : Attribute { - IntPtr GetProcAddress(Func getProcAddress); - } - - public interface IGlEntryPointAttribute - { - IntPtr GetProcAddress(TContext context, Func getProcAddress); - } - - [AttributeUsage(AttributeTargets.Property)] - public class GlOptionalEntryPoint : Attribute - { - - } - - [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] - public class GlEntryPointAttribute : Attribute, IGlEntryPointAttribute - { - public string[] EntryPoints { get; } - - public GlEntryPointAttribute(string entryPoint) - { - EntryPoints = new []{entryPoint}; - } -/* - public GlEntryPointAttribute(params string[] entryPoints) + public GlMinVersionEntryPoint(string entry, int minVersionMajor, int minVersionMinor) { - EntryPoints = entryPoints; - } -*/ - public IntPtr GetProcAddress(Func getProcAddress) - { - foreach (var name in EntryPoints) - { - var rv = getProcAddress(name); - if (rv != IntPtr.Zero) - return rv; - } - return IntPtr.Zero; - } - } - - [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] - public class GlMinVersionEntryPoint : Attribute, IGlEntryPointAttribute - { - private readonly string _entry; - private readonly GlProfileType? _profile; - private readonly int _minVersionMajor; - private readonly int _minVersionMinor; - - public GlMinVersionEntryPoint(string entry, GlProfileType profile, int minVersionMajor, - int minVersionMinor) - { - _entry = entry; - _profile = profile; - _minVersionMajor = minVersionMajor; - _minVersionMinor = minVersionMinor; } - public GlMinVersionEntryPoint(string entry, int minVersionMajor, - int minVersionMinor) + public GlMinVersionEntryPoint(string entry, int minVersionMajor, int minVersionMinor, GlProfileType profile) { - _entry = entry; - _minVersionMajor = minVersionMajor; - _minVersionMinor = minVersionMinor; } + - public IntPtr GetProcAddress(GlInterface.GlContextInfo context, Func getProcAddress) + public static IntPtr GetProcAddress(Func getProcAddress, GlInterface.GlContextInfo context, + string entry, int minVersionMajor, int minVersionMinor, GlProfileType? profile = null) { - if(_profile.HasValue && context.Version.Type != _profile) + if(profile.HasValue && context.Version.Type != profile) return IntPtr.Zero; - if(context.Version.Major<_minVersionMajor) + if(context.Version.Major + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + class GlExtensionEntryPoint : Attribute { - private readonly string _entry; - private readonly GlProfileType? _profile; - private readonly string _extension; - - public GlExtensionEntryPoint(string entry, GlProfileType profile, string extension) + public GlExtensionEntryPoint(string entry, string extension) { - _entry = entry; - _profile = profile; - _extension = extension; } - public GlExtensionEntryPoint(string entry, string extension) + public GlExtensionEntryPoint(string entry, string extension, GlProfileType profile) { - _entry = entry; - _extension = extension; } - public IntPtr GetProcAddress(GlInterface.GlContextInfo context, Func getProcAddress) + public static IntPtr GetProcAddress(Func getProcAddress, GlInterface.GlContextInfo context, + string entry, string extension, GlProfileType? profile = null) { // Ignore different profile type - if (_profile.HasValue && _profile != context.Version.Type) + if (profile.HasValue && profile != context.Version.Type) return IntPtr.Zero; // Check if extension is supported by the current context - if (!context.Extensions.Contains(_extension)) + if (!context.Extensions.Contains(extension)) return IntPtr.Zero; - return getProcAddress(_entry); + return getProcAddress(entry); } } } diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index 18bebe4cb5..d6eff9a35f 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -3,14 +3,14 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using Avalonia.Platform.Interop; +using Avalonia.SourceGenerator; using static Avalonia.OpenGL.GlConsts; namespace Avalonia.OpenGL { - public delegate IntPtr GlGetProcAddressDelegate(string procName); - - public unsafe class GlInterface : GlBasicInfoInterface + public unsafe partial class GlInterface : GlBasicInfoInterface { + private readonly Func _getProcAddress; public string Version { get; } public string Vendor { get; } public string Renderer { get; } @@ -35,12 +35,14 @@ namespace Avalonia.OpenGL } } - private GlInterface(GlContextInfo info, Func getProcAddress) : base(getProcAddress, info) + private GlInterface(GlContextInfo info, Func getProcAddress) : base(getProcAddress) { + _getProcAddress = getProcAddress; ContextInfo = info; Version = GetString(GlConsts.GL_VERSION); Renderer = GetString(GlConsts.GL_RENDERER); - Vendor = GetString(GlConsts.GL_VENDOR); + Vendor = GetString(GlConsts.GL_VENDOR); + Initialize(getProcAddress, ContextInfo); } public GlInterface(GlVersion version, Func getProcAddress) : this( @@ -48,92 +50,58 @@ namespace Avalonia.OpenGL { } - public GlInterface(GlVersion version, Func n) : this(version, ConvertNative(n)) + public IntPtr GetProcAddress(string proc) => _getProcAddress(proc); + + [GetProcAddress("glGetError")] + public partial int GetError(); + + [GetProcAddress("glClearStencil")] + public partial void ClearStencil(int s); + + [GetProcAddress("glClearColor")] + public partial void ClearColor(float r, float g, float b, float a); + + [GetProcAddress("glClear")] + public partial void Clear(int bits); + + [GetProcAddress("glViewport")] + public partial void Viewport(int x, int y, int width, int height); + + [GetProcAddress("glFlush")] + public partial void Flush(); + + [GetProcAddress("glFinish")] + public partial void Finish(); + + [GetProcAddress("glGetIntegerv")] + public partial void GetIntegerv(int name, out int rv); + + [GetProcAddress("glGenFramebuffers")] + public partial void GenFramebuffers(int count, int* res); + + public int GenFramebuffer() { - + int rv = 0; + GenFramebuffers(1, &rv); + return rv; } - public static GlInterface FromNativeUtf8GetProcAddress(GlVersion version, Func getProcAddress) => - new GlInterface(version, getProcAddress); - - - public T GetProcAddress(string proc) => Marshal.GetDelegateForFunctionPointer(GetProcAddress(proc)); - - // ReSharper disable UnassignedGetOnlyAutoProperty - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate int GlGetError(); - [GlEntryPoint("glGetError")] - public GlGetError GetError { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlClearStencil(int s); - [GlEntryPoint("glClearStencil")] - public GlClearStencil ClearStencil { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlClearColor(float r, float g, float b, float a); - [GlEntryPoint("glClearColor")] - public GlClearColor ClearColor { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlClear(int bits); - [GlEntryPoint("glClear")] - public GlClear Clear { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlViewport(int x, int y, int width, int height); - [GlEntryPoint("glViewport")] - public GlViewport Viewport { get; } - - [GlEntryPoint("glFlush")] - public UnmanagedAction Flush { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void UnmanagedAction(); - - [GlEntryPoint("glFinish")] - public UnmanagedAction Finish { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate IntPtr GlGetString(int v); - [GlEntryPoint("glGetString")] - public GlGetString GetStringNative { get; } - - public string GetString(int v) + [GetProcAddress("glDeleteFramebuffers")] + public partial void DeleteFramebuffers(int count, int* framebuffers); + + public void DeleteFramebuffer(int fb) { - var ptr = GetStringNative(v); - if (ptr != IntPtr.Zero) - return Marshal.PtrToStringAnsi(ptr); - return null; + DeleteFramebuffers(1, &fb); } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGetIntegerv(int name, out int rv); - [GlEntryPoint("glGetIntegerv")] - public GlGetIntegerv GetIntegerv { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGenFramebuffers(int count, int[] res); - [GlEntryPoint("glGenFramebuffers")] - public GlGenFramebuffers GenFramebuffers { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlDeleteFramebuffers(int count, int[] framebuffers); - [GlEntryPoint("glDeleteFramebuffers")] - public GlDeleteFramebuffers DeleteFramebuffers { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlBindFramebuffer(int target, int fb); - [GlEntryPoint("glBindFramebuffer")] - public GlBindFramebuffer BindFramebuffer { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate int GlCheckFramebufferStatus(int target); - [GlEntryPoint("glCheckFramebufferStatus")] - public GlCheckFramebufferStatus CheckFramebufferStatus { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlBlitFramebuffer(int srcX0, + [GetProcAddress("glBindFramebuffer")] + public partial void BindFramebuffer(int target, int fb); + + [GetProcAddress("glCheckFramebufferStatus")] + public partial int CheckFramebufferStatus(int target); + + [GlMinVersionEntryPoint("glBlitFramebuffer", 3, 0), GetProcAddress(true)] + public partial void BlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, @@ -143,89 +111,78 @@ namespace Avalonia.OpenGL int dstY1, int mask, int filter); - [GlMinVersionEntryPoint("glBlitFramebuffer", 3, 0), GlOptionalEntryPoint] - public GlBlitFramebuffer BlitFramebuffer { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGenRenderbuffers(int count, int[] res); - [GlEntryPoint("glGenRenderbuffers")] - public GlGenRenderbuffers GenRenderbuffers { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlDeleteRenderbuffers(int count, int[] renderbuffers); - [GlEntryPoint("glDeleteRenderbuffers")] - public GlDeleteTextures DeleteRenderbuffers { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlBindRenderbuffer(int target, int fb); - [GlEntryPoint("glBindRenderbuffer")] - public GlBindRenderbuffer BindRenderbuffer { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlRenderbufferStorage(int target, int internalFormat, int width, int height); - [GlEntryPoint("glRenderbufferStorage")] - public GlRenderbufferStorage RenderbufferStorage { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlFramebufferRenderbuffer(int target, int attachment, + + + [GetProcAddress("glGenRenderbuffers")] + public partial void GenRenderbuffers(int count, int* res); + + public int GenRenderbuffer() + { + int rv = 0; + GenRenderbuffers(1, &rv); + return rv; + } + + [GetProcAddress("glDeleteRenderbuffers")] + public partial void DeleteRenderbuffers(int count, int* renderbuffers); + + public void DeleteRenderbuffer(int renderbuffer) + { + DeleteRenderbuffers(1, &renderbuffer); + } + + [GetProcAddress("glBindRenderbuffer")] + public partial void BindRenderbuffer(int target, int fb); + + [GetProcAddress("glRenderbufferStorage")] + public partial void RenderbufferStorage(int target, int internalFormat, int width, int height); + + [GetProcAddress("glFramebufferRenderbuffer")] + public partial void FramebufferRenderbuffer(int target, int attachment, int renderbufferTarget, int renderbuffer); - [GlEntryPoint("glFramebufferRenderbuffer")] - public GlFramebufferRenderbuffer FramebufferRenderbuffer { get; } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGenTextures(int count, int[] res); - [GlEntryPoint("glGenTextures")] - public GlGenTextures GenTextures { get; } + [GetProcAddress("glGenTextures")] + public partial void GenTextures(int count, int* res); + + public int GenTexture() + { + int rv = 0; + GenTextures(1, &rv); + return rv; + } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlBindTexture(int target, int fb); - [GlEntryPoint("glBindTexture")] - public GlBindTexture BindTexture { get; } + [GetProcAddress("glBindTexture")] + public partial void BindTexture(int target, int fb); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlActiveTexture(int texture); - [GlEntryPoint("glActiveTexture")] - public GlActiveTexture ActiveTexture { get; } + [GetProcAddress("glActiveTexture")] + public partial void ActiveTexture(int texture); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlDeleteTextures(int count, int[] textures); - [GlEntryPoint("glDeleteTextures")] - public GlDeleteTextures DeleteTextures { get; } + [GetProcAddress("glDeleteTextures")] + public partial void DeleteTextures(int count, int* textures); + public void DeleteTexture(int texture) => DeleteTextures(1, &texture); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlTexImage2D(int target, int level, int internalFormat, int width, int height, int border, + [GetProcAddress("glTexImage2D")] + public partial void TexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, IntPtr data); - [GlEntryPoint("glTexImage2D")] - public GlTexImage2D TexImage2D { get; } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, + [GetProcAddress("glCopyTexSubImage2D")] + public partial void CopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, int width, int height); - - [GlEntryPoint("glCopyTexSubImage2D")] - public GlCopyTexSubImage2D CopyTexSubImage2D { get; } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlTexParameteri(int target, int name, int value); - [GlEntryPoint("glTexParameteri")] - public GlTexParameteri TexParameteri { get; } + [GetProcAddress("glTexParameteri")] + public partial void TexParameteri(int target, int name, int value); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlFramebufferTexture2D(int target, int attachment, + + [GetProcAddress("glFramebufferTexture2D")] + public partial void FramebufferTexture2D(int target, int attachment, int texTarget, int texture, int level); - [GlEntryPoint("glFramebufferTexture2D")] - public GlFramebufferTexture2D FramebufferTexture2D { get; } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate int GlCreateShader(int shaderType); - [GlEntryPoint("glCreateShader")] - public GlCreateShader CreateShader { get; } + [GetProcAddress("glCreateShader")] + public partial int CreateShader(int shaderType); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlShaderSource(int shader, int count, IntPtr strings, IntPtr lengths); - [GlEntryPoint("glShaderSource")] - public GlShaderSource ShaderSource { get; } + [GetProcAddress("glShaderSource")] + public partial void ShaderSource(int shader, int count, IntPtr strings, IntPtr lengths); public void ShaderSourceString(int shader, string source) { @@ -237,20 +194,14 @@ namespace Avalonia.OpenGL } } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlCompileShader(int shader); - [GlEntryPoint("glCompileShader")] - public GlCompileShader CompileShader { get; } + [GetProcAddress("glCompileShader")] + public partial void CompileShader(int shader); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGetShaderiv(int shader, int name, int* parameters); - [GlEntryPoint("glGetShaderiv")] - public GlGetShaderiv GetShaderiv { get; } + [GetProcAddress("glGetShaderiv")] + public partial void GetShaderiv(int shader, int name, int* parameters); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGetShaderInfoLog(int shader, int maxLength, out int length, void*infoLog); - [GlEntryPoint("glGetShaderInfoLog")] - public GlGetShaderInfoLog GetShaderInfoLog { get; } + [GetProcAddress("glGetShaderInfoLog")] + public partial void GetShaderInfoLog(int shader, int maxLength, out int length, void* infoLog); public unsafe string CompileShaderAndGetError(int shader, string source) { @@ -268,33 +219,24 @@ namespace Avalonia.OpenGL int len; fixed (void* ptr = logData) GetShaderInfoLog(shader, logLength, out len, ptr); - return Encoding.UTF8.GetString(logData,0, len); + return Encoding.UTF8.GetString(logData, 0, len); } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate int GlCreateProgram(); - [GlEntryPoint("glCreateProgram")] - public GlCreateProgram CreateProgram { get; } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlAttachShader(int program, int shader); - [GlEntryPoint("glAttachShader")] - public GlAttachShader AttachShader { get; } + [GetProcAddress("glCreateProgram")] + public partial int CreateProgram(); + + [GetProcAddress("glAttachShader")] + public partial void AttachShader(int program, int shader); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlLinkProgram(int program); - [GlEntryPoint("glLinkProgram")] - public GlLinkProgram LinkProgram { get; } + [GetProcAddress("glLinkProgram")] + public partial void LinkProgram(int program); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGetProgramiv(int program, int name, int* parameters); - [GlEntryPoint("glGetProgramiv")] - public GlGetProgramiv GetProgramiv { get; } + [GetProcAddress("glGetProgramiv")] + public partial void GetProgramiv(int program, int name, int* parameters); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGetProgramInfoLog(int program, int maxLength, out int len, void* infoLog); - [GlEntryPoint("glGetProgramInfoLog")] - public GlGetProgramInfoLog GetProgramInfoLog { get; } + [GetProcAddress("glGetProgramInfoLog")] + public partial void GetProgramInfoLog(int program, int maxLength, out int len, void* infoLog); public unsafe string LinkProgramAndGetError(int program) { @@ -309,13 +251,11 @@ namespace Avalonia.OpenGL int len; fixed (void* ptr = logData) GetProgramInfoLog(program, logLength, out len, ptr); - return Encoding.UTF8.GetString(logData,0, len); + return Encoding.UTF8.GetString(logData, 0, len); } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlBindAttribLocation(int program, int index, IntPtr name); - [GlEntryPoint("glBindAttribLocation")] - public GlBindAttribLocation BindAttribLocation { get; } + [GetProcAddress("glBindAttribLocation")] + public partial void BindAttribLocation(int program, int index, IntPtr name); public void BindAttribLocationString(int program, int index, string name) { @@ -323,32 +263,24 @@ namespace Avalonia.OpenGL BindAttribLocation(program, index, b.DangerousGetHandle()); } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlGenBuffers(int len, int[] rv); - [GlEntryPoint("glGenBuffers")] - public GlGenBuffers GenBuffers { get; } + [GetProcAddress("glGenBuffers")] + public partial void GenBuffers(int len, int* rv); public int GenBuffer() { - var rv = new int[1]; - GenBuffers(1, rv); - return rv[0]; + int rv; + GenBuffers(1, &rv); + return rv; } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlBindBuffer(int target, int buffer); - [GlEntryPoint("glBindBuffer")] - public GlBindBuffer BindBuffer { get; } + [GetProcAddress("glBindBuffer")] + public partial void BindBuffer(int target, int buffer); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlBufferData(int target, IntPtr size, IntPtr data, int usage); - [GlEntryPoint("glBufferData")] - public GlBufferData BufferData { get; } + [GetProcAddress("glBufferData")] + public partial void BufferData(int target, IntPtr size, IntPtr data, int usage); - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate int GlGetAttribLocation(int program, IntPtr name); - [GlEntryPoint("glGetAttribLocation")] - public GlGetAttribLocation GetAttribLocation { get; } + [GetProcAddress("glGetAttribLocation")] + public partial int GetAttribLocation(int program, IntPtr name); public int GetAttribLocationString(int program, string name) { @@ -356,36 +288,24 @@ namespace Avalonia.OpenGL return GetAttribLocation(program, b.DangerousGetHandle()); } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlVertexAttribPointer(int index, int size, int type, + [GetProcAddress("glVertexAttribPointer")] + public partial void VertexAttribPointer(int index, int size, int type, int normalized, int stride, IntPtr pointer); - [GlEntryPoint("glVertexAttribPointer")] - public GlVertexAttribPointer VertexAttribPointer { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlEnableVertexAttribArray(int index); - [GlEntryPoint("glEnableVertexAttribArray")] - public GlEnableVertexAttribArray EnableVertexAttribArray { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlUseProgram(int program); - [GlEntryPoint("glUseProgram")] - public GlUseProgram UseProgram { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlDrawArrays(int mode, int first, IntPtr count); - [GlEntryPoint("glDrawArrays")] - public GlDrawArrays DrawArrays { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlDrawElements(int mode, int count, int type, IntPtr indices); - [GlEntryPoint("glDrawElements")] - public GlDrawElements DrawElements { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate int GlGetUniformLocation(int program, IntPtr name); - [GlEntryPoint("glGetUniformLocation")] - public GlGetUniformLocation GetUniformLocation { get; } + + [GetProcAddress("glEnableVertexAttribArray")] + public partial void EnableVertexAttribArray(int index); + + [GetProcAddress("glUseProgram")] + public partial void UseProgram(int program); + + [GetProcAddress("glDrawArrays")] + public partial void DrawArrays(int mode, int first, IntPtr count); + + [GetProcAddress("glDrawElements")] + public partial void DrawElements(int mode, int count, int type, IntPtr indices); + + [GetProcAddress("glGetUniformLocation")] + public partial int GetUniformLocation(int program, IntPtr name); public int GetUniformLocationString(int program, string name) { @@ -393,41 +313,65 @@ namespace Avalonia.OpenGL return GetUniformLocation(program, b.DangerousGetHandle()); } - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlUniform1f(int location, float falue); - [GlEntryPoint("glUniform1f")] - public GlUniform1f Uniform1f { get; } - - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlUniformMatrix4fv(int location, int count, bool transpose, void* value); - [GlEntryPoint("glUniformMatrix4fv")] - public GlUniformMatrix4fv UniformMatrix4fv { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlEnable(int what); - [GlEntryPoint("glEnable")] - public GlEnable Enable { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlDeleteBuffers(int count, int[] buffers); - [GlEntryPoint("glDeleteBuffers")] - public GlDeleteBuffers DeleteBuffers { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlDeleteProgram(int program); - [GlEntryPoint("glDeleteProgram")] - public GlDeleteProgram DeleteProgram { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GlDeleteShader(int shader); - [GlEntryPoint("glDeleteShader")] - public GlDeleteShader DeleteShader { get; } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate void GLGetRenderbufferParameteriv(int target, int name, int[] value); - [GlEntryPoint("glGetRenderbufferParameteriv")] - public GLGetRenderbufferParameteriv GetRenderbufferParameteriv { get; } + [GetProcAddress("glUniform1f")] + public partial void Uniform1f(int location, float falue); + + + [GetProcAddress("glUniformMatrix4fv")] + public partial void UniformMatrix4fv(int location, int count, bool transpose, void* value); + + [GetProcAddress("glEnable")] + public partial void Enable(int what); + + [GetProcAddress("glDeleteBuffers")] + public partial void DeleteBuffers(int count, int* buffers); + + public void DeleteBuffer(int buffer) => DeleteBuffers(1, &buffer); + + [GetProcAddress("glDeleteProgram")] + public partial void DeleteProgram(int program); + + [GetProcAddress("glDeleteShader")] + public partial void DeleteShader(int shader); + + [GetProcAddress("glGetRenderbufferParameteriv")] + public partial void GLGetRenderbufferParameteriv(int target, int name, int* value); // ReSharper restore UnassignedGetOnlyAutoProperty + + [GetProcAddress(true)] + [GlMinVersionEntryPoint("glDeleteVertexArrays", 3, 0)] + [GlExtensionEntryPoint("glDeleteVertexArraysOES", "GL_OES_vertex_array_object")] + public partial void DeleteVertexArrays(int count, int* arrays); + + public void DeleteVertexArray(int array) => DeleteVertexArrays(1, &array); + + [GetProcAddress(true)] + [GlMinVersionEntryPoint("glBindVertexArray", 3, 0)] + [GlExtensionEntryPoint("glBindVertexArrayOES", "GL_OES_vertex_array_object")] + public partial void BindVertexArray(int array); + + + [GetProcAddress(true)] + [GlMinVersionEntryPoint("glGenVertexArrays", 3, 0)] + [GlExtensionEntryPoint("glGenVertexArraysOES", "GL_OES_vertex_array_object")] + public partial void GenVertexArrays(int n, int* rv); + + public int GenVertexArray() + { + int rv = 0; + GenVertexArrays(1, &rv); + return rv; + } + + public static GlInterface FromNativeUtf8GetProcAddress(GlVersion version, Func getProcAddress) + { + return new GlInterface(version, s => + { + var ptr = Marshal.StringToHGlobalAnsi(s); + var rv = getProcAddress(ptr); + Marshal.FreeHGlobal(ptr); + return rv; + }); + } } -} +} \ No newline at end of file diff --git a/src/Avalonia.OpenGL/GlInterfaceBase.cs b/src/Avalonia.OpenGL/GlInterfaceBase.cs deleted file mode 100644 index e7dd440e36..0000000000 --- a/src/Avalonia.OpenGL/GlInterfaceBase.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using Avalonia.Platform.Interop; - -namespace Avalonia.OpenGL -{ - public class GlInterfaceBase : GlInterfaceBase - { - public GlInterfaceBase(Func getProcAddress) : base(getProcAddress, null) - { - } - - public GlInterfaceBase(Func nativeGetProcAddress) : base(nativeGetProcAddress, null) - { - } - } - - public class GlInterfaceBase - { - private readonly Func _getProcAddress; - public GlInterfaceBase(Func getProcAddress, TContext context) - { - _getProcAddress = getProcAddress; - foreach (var prop in this.GetType().GetProperties()) - { - var attrs = prop.GetCustomAttributes() - .Where(a => - a is IGlEntryPointAttribute || a is IGlEntryPointAttribute) - .ToList(); - if(attrs.Count == 0) - continue; - - var isOptional = prop.GetCustomAttribute() != null; - - var fieldName = $"<{prop.Name}>k__BackingField"; - var field = prop.DeclaringType.GetField(fieldName, - BindingFlags.Instance | BindingFlags.NonPublic); - if (field == null) - throw new InvalidProgramException($"Expected property {prop.Name} to have {fieldName}"); - - - IntPtr proc = IntPtr.Zero; - foreach (var attr in attrs) - { - if (attr is IGlEntryPointAttribute typed) - proc = typed.GetProcAddress(context, getProcAddress); - else if (attr is IGlEntryPointAttribute untyped) - proc = untyped.GetProcAddress(getProcAddress); - if (proc != IntPtr.Zero) - break; - } - - if (proc != IntPtr.Zero) - field.SetValue(this, Marshal.GetDelegateForFunctionPointer(proc, prop.PropertyType)); - else if (!isOptional) - throw new OpenGlException("Unable to find a suitable GL function for " + prop.Name); - } - } - - protected static Func ConvertNative(Func func) => - (proc) => - { - using (var u = new Utf8Buffer(proc)) - { - var rv = func(u); - return rv; - } - }; - - public GlInterfaceBase(Func nativeGetProcAddress, TContext context) : this(ConvertNative(nativeGetProcAddress), context) - { - - } - - public IntPtr GetProcAddress(string proc) => _getProcAddress(proc); - } -} diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj index 45a76bc3d6..621e6fabd7 100644 --- a/src/Avalonia.X11/Avalonia.X11.csproj +++ b/src/Avalonia.X11/Avalonia.X11.csproj @@ -11,5 +11,5 @@ - + diff --git a/src/Avalonia.X11/Glx/Glx.cs b/src/Avalonia.X11/Glx/Glx.cs index 37ca7f5603..1a26d7d5f5 100644 --- a/src/Avalonia.X11/Glx/Glx.cs +++ b/src/Avalonia.X11/Glx/Glx.cs @@ -4,111 +4,97 @@ using System.Linq; using System.Runtime.InteropServices; using Avalonia.OpenGL; using Avalonia.Platform.Interop; +using Avalonia.SourceGenerator; + // ReSharper disable UnassignedGetOnlyAutoProperty namespace Avalonia.X11.Glx { - unsafe class GlxInterface : GlInterfaceBase + unsafe partial class GlxInterface { private const string libGL = "libGL.so.1"; - [GlEntryPointAttribute("glXMakeContextCurrent")] - public GlxMakeContextCurrent MakeContextCurrent { get; } - public delegate bool GlxMakeContextCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context); + [GetProcAddress("glXMakeContextCurrent")] + public partial bool MakeContextCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context); - [GlEntryPoint("glXGetCurrentContext")] - public GlxGetCurrentContext GetCurrentContext { get; } - public delegate IntPtr GlxGetCurrentContext(); + [GetProcAddress("glXGetCurrentContext")] + public partial IntPtr GetCurrentContext(); - [GlEntryPoint("glXGetCurrentDisplay")] - public GlxGetCurrentDisplay GetCurrentDisplay { get; } - public delegate IntPtr GlxGetCurrentDisplay(); + [GetProcAddress("glXGetCurrentDisplay")] + public partial IntPtr GetCurrentDisplay(); - [GlEntryPoint("glXGetCurrentDrawable")] - public GlxGetCurrentDrawable GetCurrentDrawable { get; } - public delegate IntPtr GlxGetCurrentDrawable(); + [GetProcAddress("glXGetCurrentDrawable")] + public partial IntPtr GetCurrentDrawable(); - [GlEntryPoint("glXGetCurrentReadDrawable")] - public GlxGetCurrentReadDrawable GetCurrentReadDrawable { get; } - public delegate IntPtr GlxGetCurrentReadDrawable(); + [GetProcAddress("glXGetCurrentReadDrawable")] + public partial IntPtr GetCurrentReadDrawable(); - [GlEntryPoint("glXCreatePbuffer")] - public GlxCreatePbuffer CreatePbuffer { get; } - public delegate IntPtr GlxCreatePbuffer(IntPtr dpy, IntPtr fbc, int[] attrib_list); + [GetProcAddress("glXCreatePbuffer")] + public partial IntPtr CreatePbuffer(IntPtr dpy, IntPtr fbc, int[] attrib_list); - [GlEntryPoint("glXDestroyPbuffer")] - public GlxDestroyPbuffer DestroyPbuffer { get; } - public delegate IntPtr GlxDestroyPbuffer(IntPtr dpy, IntPtr fb); + [GetProcAddress("glXDestroyPbuffer")] + public partial IntPtr DestroyPbuffer(IntPtr dpy, IntPtr fb); - [GlEntryPointAttribute("glXChooseVisual")] - public GlxChooseVisual ChooseVisual { get; } - public delegate XVisualInfo* GlxChooseVisual(IntPtr dpy, int screen, int[] attribList); + [GetProcAddress("glXChooseVisual")] + public partial XVisualInfo* ChooseVisual(IntPtr dpy, int screen, int[] attribList); - [GlEntryPointAttribute("glXCreateContext")] - public GlxCreateContext CreateContext { get; } - public delegate IntPtr GlxCreateContext(IntPtr dpy, XVisualInfo* vis, IntPtr shareList, bool direct); + [GetProcAddress("glXCreateContext")] + public partial IntPtr CreateContext(IntPtr dpy, XVisualInfo* vis, IntPtr shareList, bool direct); - [GlEntryPointAttribute("glXCreateContextAttribsARB")] - public GlxCreateContextAttribsARB CreateContextAttribsARB { get; } - public delegate IntPtr GlxCreateContextAttribsARB(IntPtr dpy, IntPtr fbconfig, IntPtr shareList, + [GetProcAddress("glXCreateContextAttribsARB")] + public partial IntPtr CreateContextAttribsARB(IntPtr dpy, IntPtr fbconfig, IntPtr shareList, bool direct, int[] attribs); [DllImport(libGL, EntryPoint = "glXGetProcAddress")] - public static extern IntPtr GlxGetProcAddress(Utf8Buffer buffer); + public static extern IntPtr GlxGetProcAddress(string buffer); - [GlEntryPointAttribute("glXDestroyContext")] - public GlxDestroyContext DestroyContext { get; } - public delegate void GlxDestroyContext(IntPtr dpy, IntPtr ctx); + [GetProcAddress("glXDestroyContext")] + public partial void DestroyContext(IntPtr dpy, IntPtr ctx); - [GlEntryPointAttribute("glXChooseFBConfig")] - public GlxChooseFBConfig ChooseFBConfig { get; } - public delegate IntPtr* GlxChooseFBConfig(IntPtr dpy, int screen, int[] attrib_list, out int nelements); + [GetProcAddress("glXChooseFBConfig")] + public partial IntPtr* ChooseFBConfig(IntPtr dpy, int screen, int[] attrib_list, out int nelements); - public IntPtr* GlxChooseFbConfig(IntPtr dpy, int screen, IEnumerable attribs, out int nelements) + public IntPtr* ChooseFbConfig(IntPtr dpy, int screen, IEnumerable attribs, out int nelements) { var arr = attribs.Concat(new[]{0}).ToArray(); return ChooseFBConfig(dpy, screen, arr, out nelements); } - [GlEntryPointAttribute("glXGetVisualFromFBConfig")] - public GlxGetVisualFromFBConfig GetVisualFromFBConfig { get; } - public delegate XVisualInfo * GlxGetVisualFromFBConfig(IntPtr dpy, IntPtr config); + [GetProcAddress("glXGetVisualFromFBConfig")] + public partial XVisualInfo * GetVisualFromFBConfig(IntPtr dpy, IntPtr config); - [GlEntryPointAttribute("glXGetFBConfigAttrib")] - public GlxGetFBConfigAttrib GetFBConfigAttrib { get; } - public delegate int GlxGetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); + [GetProcAddress("glXGetFBConfigAttrib")] + public partial int GetFBConfigAttrib(IntPtr dpy, IntPtr config, int attribute, out int value); - [GlEntryPointAttribute("glXSwapBuffers")] - public GlxSwapBuffers SwapBuffers { get; } - public delegate void GlxSwapBuffers(IntPtr dpy, IntPtr drawable); + [GetProcAddress("glXSwapBuffers")] + public partial void SwapBuffers(IntPtr dpy, IntPtr drawable); - [GlEntryPointAttribute("glXWaitX")] - public GlxWaitX WaitX { get; } - public delegate void GlxWaitX(); + [GetProcAddress("glXWaitX")] + public partial void WaitX(); - [GlEntryPointAttribute("glXWaitGL")] - public GlxWaitGL WaitGL { get; } - public delegate void GlxWaitGL(); + [GetProcAddress("glXWaitGL")] + public partial void WaitGL(); - public delegate int GlGetError(); - [GlEntryPoint("glGetError")] - public GlGetError GetError { get; } - public delegate IntPtr GlxQueryExtensionsString(IntPtr display, int screen); - [GlEntryPoint("glXQueryExtensionsString")] - public GlxQueryExtensionsString QueryExtensionsString { get; } + [GetProcAddress("glGetError")] + public partial int GlGetError(); + + + [GetProcAddress("glXQueryExtensionsString")] + public partial IntPtr QueryExtensionsString(IntPtr display, int screen); - public GlxInterface() : base(SafeGetProcAddress) + public GlxInterface() { + Initialize(SafeGetProcAddress); } // Ignores egl functions. @@ -122,10 +108,9 @@ namespace Avalonia.X11.Glx return IntPtr.Zero; } - return GlxConverted(proc); + return GlxGetProcAddress(proc); } - private static readonly Func GlxConverted = ConvertNative(GlxGetProcAddress); public string[] GetExtensions(IntPtr display) { diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs index 46a985c0e8..99c4b62716 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs @@ -50,7 +50,7 @@ namespace Avalonia.LinuxFramebuffer.Output } [DllImport("libEGL.so.1")] - static extern IntPtr eglGetProcAddress(Utf8Buffer proc); + static extern IntPtr eglGetProcAddress(string proc); private GbmBoUserDataDestroyCallbackDelegate FbDestroyDelegate; private drmModeModeInfo _mode; diff --git a/src/Shared/SourceGeneratorAttributes.cs b/src/Shared/SourceGeneratorAttributes.cs index fdb5977d23..ac7c82c469 100644 --- a/src/Shared/SourceGeneratorAttributes.cs +++ b/src/Shared/SourceGeneratorAttributes.cs @@ -14,4 +14,28 @@ namespace Avalonia.SourceGenerator public string Namespace { get; } public Type BaseType { get; } } + + + internal class GetProcAddressAttribute : Attribute + { + public GetProcAddressAttribute(string proc) + { + + } + + public GetProcAddressAttribute(string proc, bool optional = false) + { + + } + + public GetProcAddressAttribute(bool optional) + { + + } + + public GetProcAddressAttribute() + { + + } + } } diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs index 8ab275df63..984c1785b7 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs @@ -27,16 +27,13 @@ namespace Avalonia.Skia gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderbuffer); gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture); - var arr = new int[2]; - + // Generate FBO - gl.GenFramebuffers(1, arr); - _fbo = arr[0]; + _fbo = gl.GenFramebuffer(); gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo); // Create a texture to render into - gl.GenTextures(1, arr); - _texture = arr[0]; + _texture = gl.GenTexture(); gl.BindTexture(GL_TEXTURE_2D, _texture); gl.TexImage2D(GL_TEXTURE_2D, 0, InternalFormat, pixelSize.Width, pixelSize.Height, @@ -48,8 +45,7 @@ namespace Avalonia.Skia var success = false; foreach (var useStencil8 in TrueFalse) { - gl.GenRenderbuffers(1, arr); - _depthStencil = arr[0]; + _depthStencil = gl.GenRenderbuffer(); gl.BindRenderbuffer(GL_RENDERBUFFER, _depthStencil); if (useStencil8) @@ -73,7 +69,7 @@ namespace Avalonia.Skia else { gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderbuffer); - gl.DeleteRenderbuffers(1, arr); + gl.DeleteRenderbuffer(_depthStencil); } } @@ -83,10 +79,8 @@ namespace Avalonia.Skia if (!success) { - arr[0] = _fbo; - gl.DeleteFramebuffers(1, arr); - arr[0] = _texture; - gl.DeleteTextures(1, arr); + gl.DeleteFramebuffer(_fbo); + gl.DeleteTexture(_texture); throw new OpenGlException("Unable to create FBO with stencil"); } @@ -94,7 +88,7 @@ namespace Avalonia.Skia new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat())); Surface = SKSurface.Create(_grContext, target, surfaceOrigin, SKColorType.Rgba8888); - CanBlit = gl.BlitFramebuffer != null; + CanBlit = gl.IsBlitFramebufferAvailable; } public void Dispose() @@ -106,9 +100,9 @@ namespace Avalonia.Skia var gl = _glContext.GlInterface; if (_fbo != 0) { - gl.DeleteFramebuffers(1, new[] { _fbo }); - gl.DeleteTextures(1, new[] { _texture }); - gl.DeleteRenderbuffers(1, new[] { _depthStencil }); + gl.DeleteFramebuffer(_fbo); + gl.DeleteTexture(_texture); + gl.DeleteRenderbuffer(_depthStencil); _fbo = _texture = _depthStencil = 0; } } diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs index ec8a8436e1..08e0fbd808 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs @@ -54,7 +54,7 @@ namespace Avalonia.Skia return null; // Blit feature requires glBlitFramebuffer - if (_glContext.GlInterface.BlitFramebuffer == null) + if (!_glContext.GlInterface.IsBlitFramebufferAvailable) return null; size = new PixelSize(Math.Max(size.Width, 1), Math.Max(size.Height, 1)); diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs index 2ebf7c680b..a4617bb4d5 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs @@ -101,7 +101,7 @@ namespace Avalonia.Skia private bool _disposed; private readonly DisposableLock _lock = new DisposableLock(); - public SharedOpenGlBitmapAttachment(GlOpenGlBitmapImpl bitmap, IGlContext context, Action presentCallback) + public unsafe SharedOpenGlBitmapAttachment(GlOpenGlBitmapImpl bitmap, IGlContext context, Action presentCallback) { _bitmap = bitmap; _context = context; @@ -119,7 +119,8 @@ namespace Avalonia.Skia var gl = _context.GlInterface; var textures = new int[2]; - gl.GenTextures(2, textures); + fixed (int* ptex = textures) + gl.GenTextures(2, ptex); _texture = textures[0]; _frontBuffer = textures[1]; @@ -178,7 +179,7 @@ namespace Avalonia.Skia _presentCallback(); } - public void Dispose() + public unsafe void Dispose() { var gl = _context.GlInterface; _bitmap.Present(null); @@ -191,7 +192,9 @@ namespace Avalonia.Skia if(_disposed) return; _disposed = true; - gl.DeleteTextures(2, new[] { _texture, _frontBuffer }); + var tex = new[] { _texture, _frontBuffer }; + fixed (int* ptex = tex) + gl.DeleteTextures(2, ptex); } } diff --git a/src/tools/DevGenerators/GetProcAddressInitialization.cs b/src/tools/DevGenerators/GetProcAddressInitialization.cs new file mode 100644 index 0000000000..3b3871370c --- /dev/null +++ b/src/tools/DevGenerators/GetProcAddressInitialization.cs @@ -0,0 +1,346 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Generator; + +[Generator(LanguageNames.CSharp)] +public class GetProcAddressInitializationGenerator : IIncrementalGenerator +{ + const string GetProcAddressFullName = "global::Avalonia.SourceGenerator.GetProcAddressAttribute"; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + /* + Console.WriteLine("PID: " + Process.GetCurrentProcess().Id); + + File.WriteAllText("/tmp/pid", "PID: " + Process.GetCurrentProcess().Id); + while (!Debugger.IsAttached) + { + Thread.Sleep(1000); + }*/ + var allMethodsWithAttributes = context.SyntaxProvider + .CreateSyntaxProvider( + static (s, _) => s is MethodDeclarationSyntax + { + AttributeLists.Count: > 0, + } md && md.Modifiers.Any(m=>m.IsKind(SyntaxKind.PartialKeyword)), + static (context, _) => + (IMethodSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!); + + var fieldsWithAttribute = allMethodsWithAttributes + .Where(s => s.HasAttributeWithFullyQualifiedName(GetProcAddressFullName)); + + var all = fieldsWithAttribute.Collect(); + context.RegisterSourceOutput(all, static (context, methods) => + { + foreach (var typeGroup in methods.GroupBy(f => f.ContainingType)) + { + var nextContext = 0; + var contexts = new Dictionary(); + + string GetContextNameFromIndex(int c) => "context" + (c == 0 ? "" : c); + string GetContextName(string type) + { + if (contexts.TryGetValue(type, out var idx)) + return GetContextNameFromIndex(idx); + if (nextContext != 0) + idx += nextContext; + nextContext++; + return GetContextNameFromIndex(contexts[type] = idx); + } + + var classBuilder = new StringBuilder(); + if (typeGroup.Key.ContainingNamespace != null) + classBuilder + .AppendLine("using System;") + .Append("namespace ") + .Append(typeGroup.Key.ContainingNamespace) + .AppendLine(";"); + classBuilder + .Append("unsafe partial class ") + .AppendLine(typeGroup.Key.Name) + .AppendLine("{"); + var initializeBody = new StringBuilder() + .Pad(2) + .AppendLine("var addr = IntPtr.Zero;"); + + foreach (var method in typeGroup) + { + var isOptional = false; + var first = true; + var fieldName = "_addr_" + method.Name; + var delegateType = BuildDelegateType(method); + + void AppendNextAddr() + { + if (first) + { + first = false; + initializeBody.Pad(2); + } + else + initializeBody + .Pad(2) + .Append("if(addr == IntPtr.Zero) "); + } + + initializeBody + .Pad(2).Append("// Initializing ").AppendLine(method.Name) + .Pad(2) + .AppendLine("addr = IntPtr.Zero;"); + foreach (var attr in method.GetAttributes()) + { + if (attr.AttributeClass?.HasFullyQualifiedName(GetProcAddressFullName) == true) + { + string? primaryName = null; + foreach (var arg in attr.ConstructorArguments) + { + if (arg.Value is string name) + primaryName = name; + if (arg.Value is bool opt) + isOptional = opt; + } + + if (primaryName != null) + { + AppendNextAddr(); + initializeBody + .Append("addr = getProcAddress(\"") + .Append(primaryName) + .AppendLine("\");"); + } + } + else + { + if (attr.AttributeClass != null + && attr.AttributeClass.MemberNames.Contains("GetProcAddress")) + { + var getProcMethod = attr.AttributeClass.GetMembers() + .FirstOrDefault(m => m.Name == "GetProcAddress") as IMethodSymbol; + if (getProcMethod == null || !getProcMethod.IsStatic || getProcMethod.Parameters.Length < 2) + continue; + var contextName = + GetContextName(getProcMethod + .Parameters[1].Type + .ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + AppendNextAddr(); + initializeBody + .Append("addr = ") + .Append(attr.AttributeClass.GetFullyQualifiedName()) + .Append(".GetProcAddress(") + .Append("getProcAddress, ") + .Append(contextName); + + var syntaxNode = (AttributeSyntax)attr.ApplicationSyntaxReference.GetSyntax(); + foreach (var arg in syntaxNode.ArgumentList.Arguments) + initializeBody.Append(", ").Append(arg.GetText()); + initializeBody.AppendLine(");"); + } + } + } + + if (!isOptional) + { + initializeBody + .Pad(2) + .Append("if (addr == IntPtr.Zero) throw new System.EntryPointNotFoundException(\"") + .Append(fieldName).AppendLine("\");"); + } + + initializeBody + .Pad(2) + .Append(fieldName) + .Append(" = (") + .Append(delegateType) + .AppendLine(")addr;"); + + classBuilder + .Pad(1) + .Append(delegateType); + classBuilder + .Append(fieldName) + .AppendLine(";"); + + classBuilder + .Pad(1) + .Append("public partial ") + .Append(method.ReturnType.GetFullyQualifiedName()) + .Append(" ") + .Append(method.Name) + .Append("("); + var firstArg = true; + foreach (var p in method.Parameters) + { + if (firstArg) + firstArg = false; + else + classBuilder.Append(", "); + AppendRefKind(classBuilder, p.RefKind); + classBuilder + .Append(p.Type.GetFullyQualifiedName()) + .Append(" @") + .Append(p.Name); + } + classBuilder + .AppendLine(")") + .Pad(1) + .AppendLine("{"); + if (isOptional) + classBuilder + .Pad(2) + .Append("if (") + .Append(fieldName) + .Append(" == null) throw new System.EntryPointNotFoundException(\"") + .Append(method.Name) + .AppendLine("\");"); + + foreach(var p in method.Parameters) + if (NeedsPin(p.Type)) + classBuilder.Pad(2) + .Append("fixed(") + .Append(MapToNative(p.Type)) + .Append(" @__p_") + .Append(p.Name) + .Append(" = ") + .Append(p.Name) + .AppendLine(")"); + + classBuilder.Pad(2); + if (!method.ReturnsVoid) + classBuilder.Append("return "); + + var invokeBuilder = new StringBuilder(); + + invokeBuilder + .Append(fieldName) + .Append("("); + firstArg = true; + foreach (var p in method.Parameters) + { + if (firstArg) + firstArg = false; + else + invokeBuilder.Append(", "); + AppendRefKind(invokeBuilder, p.RefKind); + invokeBuilder + .Append("@") + .Append(ConvertToNative(p.Name, p.Type)); + } + + invokeBuilder.Append(")"); + classBuilder.Append(ConvertToManaged(method.ReturnType, invokeBuilder.ToString())); + + classBuilder.AppendLine(";").Pad(1).AppendLine("}"); + if (isOptional) + classBuilder + .Pad(1) + .Append("public bool Is") + .Append(method.Name) + .Append("Available => ") + .Append(fieldName) + .AppendLine(" != null;"); + } + + classBuilder + .Pad(1) + .Append("void Initialize(Func getProcAddress"); + foreach (var kv in contexts.OrderBy(x => x.Value)) + { + classBuilder + .Append(", ") + .Append(kv.Key) + .Append(" ") + .Append(GetContextNameFromIndex(kv.Value)); + } + + classBuilder.AppendLine(")").Pad(1).AppendLine("{"); + classBuilder.Append(initializeBody.ToString()); + classBuilder.Append("}\n}"); + + + context.AddSource(typeGroup.Key.GetFullyQualifiedName().Replace(":", ""), classBuilder.ToString()); + } + }); + + + } + + static StringBuilder AppendRefKind(StringBuilder sb, RefKind kind) + { + if (kind == RefKind.Ref) + sb.Append("ref "); + if (kind == RefKind.Out) + sb.Append("out "); + return sb; + } + + static bool NeedsPin(ITypeSymbol type) + { + if (type.TypeKind == TypeKind.Array) + return true; + return false; + } + + static string ConvertToNative(string name, ITypeSymbol type) + { + if (NeedsPin(type)) + return "__p_" + name; + if (IsBool(type)) + return $"{name} ? 1 : 0"; + return name; + } + + static string ConvertToManaged(ITypeSymbol type, string expr) + { + if (IsBool(type)) + return expr + " != 0"; + return expr; + } + + static bool IsBool(ITypeSymbol type) => type.GetFullyQualifiedName() == "global::System.Boolean" || + type.GetFullyQualifiedName() == "bool"; + + static string MapToNative(ITypeSymbol type) + { + if (type.TypeKind == TypeKind.Array) + return ((IArrayTypeSymbol)type).ElementType.GetFullyQualifiedName() + "*"; + if (IsBool(type)) + return "int"; + return type.GetFullyQualifiedName(); + } + + static string BuildDelegateType(IMethodSymbol method) + { + StringBuilder name = new("delegate* unmanaged[Stdcall]<"); + var firstArg = true; + + void AppendArg(string a, RefKind kind) + { + if (firstArg) + firstArg = false; + else + name.Append(","); + AppendRefKind(name, kind); + name.Append(a); + } + + foreach (var p in method.Parameters) + { + AppendArg(MapToNative(p.Type), p.RefKind); + } + + AppendArg(MapToNative(method.ReturnType), RefKind.None); + name.Append(">"); + return name.ToString(); + } + +} \ No newline at end of file diff --git a/src/tools/DevGenerators/Helpers.cs b/src/tools/DevGenerators/Helpers.cs new file mode 100644 index 0000000000..3da89d2d0e --- /dev/null +++ b/src/tools/DevGenerators/Helpers.cs @@ -0,0 +1,31 @@ +using System.Collections.Immutable; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Generator; + +static class Helpers +{ + public static StringBuilder Pad(this StringBuilder sb, int count) => sb.Append(' ', count * 4); + + public static string GetFullyQualifiedName(this ISymbol symbol) + { + return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + } + + public static bool HasFullyQualifiedName(this ISymbol symbol, string name) + { + return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == name; + } + + public static bool HasAttributeWithFullyQualifiedName(this ISymbol symbol, string name) + { + ImmutableArray attributes = symbol.GetAttributes(); + + foreach (AttributeData attribute in attributes) + if (attribute.AttributeClass?.HasFullyQualifiedName(name) == true) + return true; + + return false; + } +} \ No newline at end of file