Browse Source

Use codegen to populate OpenGL functions

pull/8589/head
Nikita Tsukanov 4 years ago
parent
commit
952b80df2a
  1. 44
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  2. 4
      src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
  3. 1
      src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
  4. 14
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  5. 2
      src/Avalonia.OpenGL/Egl/EglContext.cs
  6. 4
      src/Avalonia.OpenGL/Egl/EglDisplay.cs
  7. 225
      src/Avalonia.OpenGL/Egl/EglInterface.cs
  8. 44
      src/Avalonia.OpenGL/GlBasicInfoInterface.cs
  9. 103
      src/Avalonia.OpenGL/GlEntryPointAttribute.cs
  10. 494
      src/Avalonia.OpenGL/GlInterface.cs
  11. 79
      src/Avalonia.OpenGL/GlInterfaceBase.cs
  12. 2
      src/Avalonia.X11/Avalonia.X11.csproj
  13. 111
      src/Avalonia.X11/Glx/Glx.cs
  14. 2
      src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
  15. 24
      src/Shared/SourceGeneratorAttributes.cs
  16. 28
      src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
  17. 2
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  18. 11
      src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
  19. 346
      src/tools/DevGenerators/GetProcAddressInitialization.cs
  20. 31
      src/tools/DevGenerators/Helpers.cs

44
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<GlInterface.GlContextInfo>
{
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];
}
}
}
}

4
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<AngleOptions>()?.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)
{

1
src/Avalonia.OpenGL/Avalonia.OpenGL.csproj

@ -11,4 +11,5 @@
</ItemGroup>
<Import Project="..\..\build\DevAnalyzers.props" />
<Import Project="..\..\build\SourceGenerators.props" />
</Project>

14
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);

2
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; }

4
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)

225
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<string, IntPtr> getProcAddress)
{
}
public EglInterface(Func<Utf8Buffer,IntPtr> getProcAddress) : base(getProcAddress)
{
Initialize(getProcAddress);
}
public EglInterface(Func<string, IntPtr> getProcAddress) : base(getProcAddress)
public EglInterface(string library) : this(Load(library))
{
}
public EglInterface(string library) : base(Load(library))
public EglInterface() : this(Load())
{
}
static Func<string, IntPtr> Load()
{
var os = AvaloniaLocator.Current.GetService<IRuntimePlatform>().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);
}
}

44
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<object>
public unsafe partial class GlBasicInfoInterface
{
public GlBasicInfoInterface(Func<string, IntPtr> getProcAddress) : base(getProcAddress, null)
{
}
public GlBasicInfoInterface(Func<Utf8Buffer, IntPtr> nativeGetProcAddress) : base(nativeGetProcAddress, null)
{
public GlBasicInfoInterface(Func<string, IntPtr> 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<TContextInfo> : GlInterfaceBase<TContextInfo>
{
public GlBasicInfoInterface(Func<string, IntPtr> getProcAddress, TContextInfo context) : base(getProcAddress, context)
{
}
[GetProcAddress("glGetIntegerv")]
public partial void GetIntegerv(int name, out int rv);
public GlBasicInfoInterface(Func<Utf8Buffer, IntPtr> 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)
{

103
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<string, IntPtr> getProcAddress);
}
public interface IGlEntryPointAttribute<in TContext>
{
IntPtr GetProcAddress(TContext context, Func<string, IntPtr> 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<string, IntPtr> 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<GlInterface.GlContextInfo>
{
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<string, IntPtr> getProcAddress)
public static IntPtr GetProcAddress(Func<string, IntPtr> 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<minVersionMajor)
return IntPtr.Zero;
if (context.Version.Major == _minVersionMajor && context.Version.Minor < _minVersionMinor)
if (context.Version.Major == minVersionMajor && context.Version.Minor < minVersionMinor)
return IntPtr.Zero;
return getProcAddress(_entry);
return getProcAddress(entry);
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class GlExtensionEntryPoint : Attribute, IGlEntryPointAttribute<GlInterface.GlContextInfo>
[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<string, IntPtr> getProcAddress)
public static IntPtr GetProcAddress(Func<string, IntPtr> 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);
}
}
}

494
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<GlInterface.GlContextInfo>
public unsafe partial class GlInterface : GlBasicInfoInterface
{
private readonly Func<string, IntPtr> _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<string, IntPtr> getProcAddress) : base(getProcAddress, info)
private GlInterface(GlContextInfo info, Func<string, IntPtr> 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<string, IntPtr> getProcAddress) : this(
@ -48,92 +50,58 @@ namespace Avalonia.OpenGL
{
}
public GlInterface(GlVersion version, Func<Utf8Buffer, IntPtr> 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<Utf8Buffer, IntPtr> getProcAddress) =>
new GlInterface(version, getProcAddress);
public T GetProcAddress<T>(string proc) => Marshal.GetDelegateForFunctionPointer<T>(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<IntPtr, IntPtr> getProcAddress)
{
return new GlInterface(version, s =>
{
var ptr = Marshal.StringToHGlobalAnsi(s);
var rv = getProcAddress(ptr);
Marshal.FreeHGlobal(ptr);
return rv;
});
}
}
}
}

79
src/Avalonia.OpenGL/GlInterfaceBase.cs

@ -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<object>
{
public GlInterfaceBase(Func<string, IntPtr> getProcAddress) : base(getProcAddress, null)
{
}
public GlInterfaceBase(Func<Utf8Buffer, IntPtr> nativeGetProcAddress) : base(nativeGetProcAddress, null)
{
}
}
public class GlInterfaceBase<TContext>
{
private readonly Func<string, IntPtr> _getProcAddress;
public GlInterfaceBase(Func<string, IntPtr> getProcAddress, TContext context)
{
_getProcAddress = getProcAddress;
foreach (var prop in this.GetType().GetProperties())
{
var attrs = prop.GetCustomAttributes()
.Where(a =>
a is IGlEntryPointAttribute || a is IGlEntryPointAttribute<TContext>)
.ToList();
if(attrs.Count == 0)
continue;
var isOptional = prop.GetCustomAttribute<GlOptionalEntryPoint>() != 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<TContext> 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<string, IntPtr> ConvertNative(Func<Utf8Buffer, IntPtr> func) =>
(proc) =>
{
using (var u = new Utf8Buffer(proc))
{
var rv = func(u);
return rv;
}
};
public GlInterfaceBase(Func<Utf8Buffer, IntPtr> nativeGetProcAddress, TContext context) : this(ConvertNative(nativeGetProcAddress), context)
{
}
public IntPtr GetProcAddress(string proc) => _getProcAddress(proc);
}
}

2
src/Avalonia.X11/Avalonia.X11.csproj

@ -11,5 +11,5 @@
<ProjectReference Include="..\Avalonia.FreeDesktop\Avalonia.FreeDesktop.csproj" />
<Compile Include="..\Shared\RawEventGrouping.cs" />
</ItemGroup>
<Import Project="..\..\build\SourceGenerators.props" />
</Project>

111
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<int> attribs, out int nelements)
public IntPtr* ChooseFbConfig(IntPtr dpy, int screen, IEnumerable<int> 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<string, IntPtr> GlxConverted = ConvertNative(GlxGetProcAddress);
public string[] GetExtensions(IntPtr display)
{

2
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;

24
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()
{
}
}
}

28
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;
}
}

2
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));

11
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);
}
}

346
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, int>();
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<string, IntPtr> 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();
}
}

31
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<AttributeData> attributes = symbol.GetAttributes();
foreach (AttributeData attribute in attributes)
if (attribute.AttributeClass?.HasFullyQualifiedName(name) == true)
return true;
return false;
}
}
Loading…
Cancel
Save